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_ACCESSEXPIRY "accessexpiry"
120 #define KEY_CLVLEXPIRY "clvlexpiry"
121 #define KEY_LASTLEVEL "lastlevel"
124 #define KEY_OWNER "owner"
125 #define KEY_REASON "reason"
126 #define KEY_SET "set"
127 #define KEY_DURATION "duration"
128 #define KEY_EXPIRES "expires"
129 #define KEY_TRIGGERED "triggered"
131 #define KEY_GOD_TIMEOUT "god_timeout"
133 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
134 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
136 /* Administrative messages */
137 static const struct message_entry msgtab
[] = {
138 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
140 /* Channel registration */
141 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
142 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
143 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
144 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
145 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
146 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
147 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
148 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
150 /* Do-not-register channels */
151 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
152 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
153 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
154 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
155 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
156 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
157 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
158 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
159 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
160 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
161 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
163 /* Channel unregistration */
164 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
165 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
166 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
167 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
170 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
171 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
173 /* Channel merging */
174 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
175 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
176 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
177 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
178 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
180 /* Handle unregistration */
181 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
184 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
185 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
186 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
187 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
188 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
189 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
190 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
191 { "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." },
192 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
193 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
194 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
195 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
196 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
197 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
199 /* Removing yourself from a channel. */
200 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
201 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
202 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
204 /* User management */
205 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
206 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
207 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
208 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
209 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
210 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
211 { "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." },
212 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
213 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
214 { "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." },
215 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
216 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
217 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
218 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
219 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
220 { "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" },
221 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
223 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
224 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
225 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
226 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
227 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
228 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
229 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
230 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
233 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
234 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
235 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
236 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
237 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
238 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
239 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
240 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
241 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
242 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
243 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
244 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
245 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
246 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
247 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
248 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
249 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
251 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
253 /* Channel management */
254 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
255 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
256 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
258 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
259 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
260 { "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" },
261 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
262 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
263 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
264 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
266 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
267 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
268 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
269 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
270 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
271 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
272 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
273 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
274 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
275 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
276 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
277 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
278 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
279 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
280 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
281 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
282 { "CSMSG_SET_MODES", "$bModes $b %s" },
283 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
284 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
285 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
286 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
287 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
288 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
289 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
290 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
291 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
292 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
293 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
294 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
295 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
296 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
297 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
298 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
299 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
300 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
301 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
303 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
304 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
305 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
306 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
307 { "CSMSG_USET_INFO", "$bInfo $b %s" },
309 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
310 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
311 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
312 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
313 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
314 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
315 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
316 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
317 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
318 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
319 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
321 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
322 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
323 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
324 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
325 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
326 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
327 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
329 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
330 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
331 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
332 { "CSMSG_PROTECT_NONE", "No users will be protected." },
333 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
334 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
335 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
337 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
338 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
339 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
340 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
341 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
343 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
344 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
345 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
346 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
347 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
349 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
350 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
351 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
352 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
353 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
355 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
356 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
357 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
358 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
359 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
360 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
362 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
363 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
364 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
365 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
366 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
367 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
368 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
369 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
370 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
372 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
373 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
374 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
376 /* Channel userlist */
377 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
378 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
379 /* uncomment if needed to adujust styles (and change code below)
380 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
381 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
382 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
383 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
384 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
385 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
387 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
388 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
389 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
391 /* Channel note list */
392 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
393 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
394 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
395 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
396 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
397 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
398 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
399 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
400 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
401 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
402 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
403 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
404 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
405 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
406 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
408 /* Channel [un]suspension */
409 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
410 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
411 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
412 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
413 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
414 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
415 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
417 /* Access information */
418 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
419 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
420 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
421 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
422 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
423 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
424 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
425 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
426 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
427 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
428 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
430 /* Seen information */
431 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
432 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
433 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
434 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
436 /* Names information */
437 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
438 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
440 /* Channel information */
441 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
442 { "CSMSG_BAR", "----------------------------------------"},
443 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
444 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
445 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
446 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
447 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
448 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
449 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
450 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
451 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
452 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
453 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
454 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
455 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
456 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
457 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
458 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
459 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
460 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
461 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
462 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
463 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
464 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
465 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
466 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
467 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
468 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
470 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
471 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
472 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
473 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
474 { "CSMSG_PEEK_OPS", "$bOps:$b" },
475 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
476 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
478 /* Network information */
479 { "CSMSG_NETWORK_INFO", "Network Information:" },
480 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
481 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
482 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
483 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
484 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
485 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
486 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
487 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
490 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
491 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
492 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
494 /* Channel searches */
495 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
496 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
497 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
498 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
500 /* Channel configuration */
501 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
502 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
503 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
504 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
507 { "CSMSG_USER_OPTIONS", "User Options:" },
508 // { "CSMSG_USER_PROTECTED", "That user is protected." },
511 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
512 { "CSMSG_PING_RESPONSE", "Pong!" },
513 { "CSMSG_WUT_RESPONSE", "wut" },
514 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
515 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
516 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
517 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
518 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
519 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
520 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
523 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
524 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
525 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
526 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
530 /* eject_user and unban_user flags */
531 #define ACTION_KICK 0x0001
532 #define ACTION_BAN 0x0002
533 #define ACTION_ADD_LAMER 0x0004
534 #define ACTION_ADD_TIMED_LAMER 0x0008
535 #define ACTION_UNBAN 0x0010
536 #define ACTION_DEL_LAMER 0x0020
538 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
539 #define MODELEN 40 + KEYLEN
543 #define CSFUNC_ARGS user, channel, argc, argv, cmd
545 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
546 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
547 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
548 reply("MSG_MISSING_PARAMS", argv[0]); \
552 DECLARE_LIST(dnrList
, struct do_not_register
*);
553 DEFINE_LIST(dnrList
, struct do_not_register
*);
555 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
557 struct userNode
*chanserv
;
560 extern struct string_list
*autojoin_channels
;
561 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
562 static struct log_type
*CS_LOG
;
563 struct adduserPending
* adduser_pendings
= NULL
;
564 unsigned int adduser_pendings_count
= 0;
565 unsigned long god_timeout
;
569 struct channelList support_channels
;
570 struct mod_chanmode default_modes
;
572 unsigned long db_backup_frequency
;
573 unsigned long channel_expire_frequency
;
574 unsigned long ban_timeout_frequency
;
577 unsigned int adjust_delay
;
578 long channel_expire_delay
;
579 unsigned int nodelete_level
;
581 unsigned int adjust_threshold
;
582 int join_flood_threshold
;
584 unsigned int greeting_length
;
585 unsigned int refresh_period
;
586 unsigned int giveownership_period
;
588 unsigned int max_owned
;
589 unsigned int max_chan_users
;
590 unsigned int max_chan_bans
; /* lamers */
591 unsigned int max_userinfo_length
;
593 struct string_list
*set_shows
;
594 struct string_list
*eightball
;
595 struct string_list
*old_ban_names
;
597 const char *ctcp_short_ban_duration
;
598 const char *ctcp_long_ban_duration
;
600 const char *irc_operator_epithet
;
601 const char *network_helper_epithet
;
602 const char *support_helper_epithet
;
607 struct userNode
*user
;
608 struct userNode
*bot
;
609 struct chanNode
*channel
;
611 unsigned short lowest
;
612 unsigned short highest
;
613 struct userData
**users
;
614 struct helpfile_table table
;
617 enum note_access_type
619 NOTE_SET_CHANNEL_ACCESS
,
620 NOTE_SET_CHANNEL_SETTER
,
624 enum note_visible_type
627 NOTE_VIS_CHANNEL_USERS
,
633 enum note_access_type set_access_type
;
635 unsigned int min_opserv
;
636 unsigned short min_ulevel
;
638 enum note_visible_type visible_type
;
639 unsigned int max_length
;
646 struct note_type
*type
;
647 char setter
[NICKSERV_HANDLE_LEN
+1];
651 static unsigned int registered_channels
;
652 static unsigned int banCount
;
654 static const struct {
657 unsigned short level
;
659 } accessLevels
[] = { /* MUST be orderd less to most! */
660 { "peon", "Peon", UL_PEON
, '+' },
661 { "halfop", "HalfOp", UL_HALFOP
, '%' },
662 { "op", "Op", UL_OP
, '@' },
663 { "manager", "Manager", UL_MANAGER
, '%' },
664 { "coowner", "Coowner", UL_COOWNER
, '*' },
665 { "owner", "Owner", UL_OWNER
, '!' },
666 { "helper", "BUG:", UL_HELPER
, 'X' }
669 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
670 static const struct {
673 unsigned short default_value
;
674 unsigned int old_idx
;
675 unsigned int old_flag
;
676 unsigned short flag_value
;
678 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
679 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
680 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
681 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
682 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
683 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
684 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
685 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
686 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
689 struct charOptionValues
{
692 } automodeValues
[] = {
693 { 'n', "CSMSG_AUTOMODE_NONE" },
694 { 'y', "CSMSG_AUTOMODE_NORMAL" },
695 { 'v', "CSMSG_AUTOMODE_VOICE" },
696 { 'h', "CSMSG_AUTOMODE_HOP" },
697 { 'o', "CSMSG_AUTOMODE_OP" },
698 { 'm', "CSMSG_AUTOMODE_MUTE" },
699 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
700 }, protectValues
[] = {
701 { 'a', "CSMSG_PROTECT_ALL" },
702 { 'e', "CSMSG_PROTECT_EQUAL" },
703 { 'l', "CSMSG_PROTECT_LOWER" },
704 { 'n', "CSMSG_PROTECT_NONE" }
706 { 'd', "CSMSG_TOYS_DISABLED" },
707 { 'n', "CSMSG_TOYS_PRIVATE" },
708 { 'p', "CSMSG_TOYS_PUBLIC" }
709 }, topicRefreshValues
[] = {
710 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
711 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
712 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
713 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
714 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
715 }, ctcpReactionValues
[] = {
716 { 'n', "CSMSG_CTCPREACTION_NONE" },
717 { 'k', "CSMSG_CTCPREACTION_KICK" },
718 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
719 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
720 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
721 }, banTimeoutValues
[] = {
722 { '0', "CSMSG_BANTIMEOUT_NONE" },
723 { '1', "CSMSG_BANTIMEOUT_10M" },
724 { '2', "CSMSG_BANTIMEOUT_2H" },
725 { '3', "CSMSG_BANTIMEOUT_4H" },
726 { '4', "CSMSG_BANTIMEOUT_1D" },
727 { '5', "CSMSG_BANTIMEOUT_1W" }
730 { 'n', "CSMSG_RESYNC_NEVER" },
731 { '1', "CSMSG_RESYNC_3_HOURS" },
732 { '2', "CSMSG_RESYNC_6_HOURS" },
733 { '3', "CSMSG_RESYNC_12_HOURS" },
734 { '4', "CSMSG_RESYNC_24_HOURS" }
737 static const struct {
741 unsigned int old_idx
;
743 struct charOptionValues
*values
;
745 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
746 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
747 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
748 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
749 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
750 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
751 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
754 struct userData
*helperList
;
755 struct chanData
*channelList
;
756 static struct module *chanserv_module
;
757 static unsigned int userCount
;
758 unsigned int chanserv_read_version
= 0; /* db version control */
760 #define CHANSERV_DB_VERSION 2
762 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
763 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
766 user_level_from_name(const char *name
, unsigned short clamp_level
)
768 unsigned int level
= 0, ii
;
770 level
= strtoul(name
, NULL
, 10);
771 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
772 if(!irccasecmp(name
, accessLevels
[ii
].name
))
773 level
= accessLevels
[ii
].level
;
774 if(level
> clamp_level
)
780 user_level_name_from_level(int level
)
788 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
789 if(level
>= accessLevels
[ii
].level
)
790 highest
= accessLevels
[ii
].title
;
796 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
799 *minl
= strtoul(arg
, &sep
, 10);
807 *maxl
= strtoul(sep
+1, &sep
, 10);
815 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
817 struct userData
*uData
, **head
;
819 if(!channel
|| !handle
)
822 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
823 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
825 for(uData
= helperList
;
826 uData
&& uData
->handle
!= handle
;
827 uData
= uData
->next
);
831 uData
= calloc(1, sizeof(struct userData
));
832 uData
->handle
= handle
;
834 uData
->access
= UL_HELPER
;
840 uData
->next
= helperList
;
842 helperList
->prev
= uData
;
850 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
851 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
854 head
= &(channel
->users
);
857 if(uData
&& (uData
!= *head
))
859 /* Shuffle the user to the head of whatever list he was in. */
861 uData
->next
->prev
= uData
->prev
;
863 uData
->prev
->next
= uData
->next
;
869 (**head
).prev
= uData
;
876 /* Returns non-zero if user has at least the minimum access.
877 * exempt_owner is set when handling !set, so the owner can set things
880 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
882 struct userData
*uData
;
883 struct chanData
*cData
= channel
->channel_info
;
884 unsigned short minimum
= cData
->lvlOpts
[opt
];
887 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
890 if(minimum
<= uData
->access
)
892 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
897 /* Scan for other users authenticated to the same handle
898 still in the channel. If so, keep them listed as present.
900 user is optional, if not null, it skips checking that userNode
901 (for the handle_part function) */
903 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
907 if(IsSuspended(uData
->channel
)
908 || IsUserSuspended(uData
)
909 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
921 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
923 unsigned int eflags
, argc
;
925 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
927 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
928 if(!channel
->channel_info
929 || IsSuspended(channel
->channel_info
)
931 || !ircncasecmp(text
, "ACTION ", 7))
933 /* We dont punish people we know -Rubin
934 * * Figure out the minimum level needed to CTCP the channel *
936 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
939 /* If they are a user of the channel, they are exempt */
940 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
942 /* We need to enforce against them; do so. */
945 argv
[1] = user
->nick
;
947 if(GetUserMode(channel
, user
))
948 eflags
|= ACTION_KICK
;
949 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
950 default: case 'n': return;
952 eflags
|= ACTION_KICK
;
955 eflags
|= ACTION_BAN
;
958 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
959 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
962 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
963 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
966 argv
[argc
++] = bad_ctcp_reason
;
967 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
971 chanserv_create_note_type(const char *name
)
973 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
974 strcpy(ntype
->name
, name
);
976 dict_insert(note_types
, ntype
->name
, ntype
);
981 chanserv_deref_note_type(void *data
)
983 struct note_type
*ntype
= data
;
985 if(--ntype
->refs
> 0)
991 chanserv_flush_note_type(struct note_type
*ntype
)
993 struct chanData
*cData
;
994 for(cData
= channelList
; cData
; cData
= cData
->next
)
995 dict_remove(cData
->notes
, ntype
->name
);
999 chanserv_truncate_notes(struct note_type
*ntype
)
1001 struct chanData
*cData
;
1003 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1005 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1006 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1009 if(strlen(note
->note
) <= ntype
->max_length
)
1011 dict_remove2(cData
->notes
, ntype
->name
, 1);
1012 note
= realloc(note
, size
);
1013 note
->note
[ntype
->max_length
] = 0;
1014 dict_insert(cData
->notes
, ntype
->name
, note
);
1018 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1020 static struct note
*
1021 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1024 unsigned int len
= strlen(text
);
1026 if(len
> type
->max_length
) len
= type
->max_length
;
1027 note
= calloc(1, sizeof(*note
) + len
);
1029 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1030 memcpy(note
->note
, text
, len
);
1031 note
->note
[len
] = 0;
1032 dict_insert(channel
->notes
, type
->name
, note
);
1038 chanserv_free_note(void *data
)
1040 struct note
*note
= data
;
1042 chanserv_deref_note_type(note
->type
);
1043 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1047 static MODCMD_FUNC(cmd_createnote
) {
1048 struct note_type
*ntype
;
1049 unsigned int arg
= 1, existed
= 0, max_length
;
1051 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1054 ntype
= chanserv_create_note_type(argv
[arg
]);
1055 if(!irccasecmp(argv
[++arg
], "privileged"))
1058 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1059 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1061 else if(!irccasecmp(argv
[arg
], "channel"))
1063 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1066 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1069 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1070 ntype
->set_access
.min_ulevel
= ulvl
;
1072 else if(!irccasecmp(argv
[arg
], "setter"))
1074 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1078 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1082 if(!irccasecmp(argv
[++arg
], "privileged"))
1083 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1084 else if(!irccasecmp(argv
[arg
], "channel_users"))
1085 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1086 else if(!irccasecmp(argv
[arg
], "all"))
1087 ntype
->visible_type
= NOTE_VIS_ALL
;
1089 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1093 if((arg
+1) >= argc
) {
1094 reply("MSG_MISSING_PARAMS", argv
[0]);
1097 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1098 if(max_length
< 20 || max_length
> 450)
1100 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1103 if(existed
&& (max_length
< ntype
->max_length
))
1105 ntype
->max_length
= max_length
;
1106 chanserv_truncate_notes(ntype
);
1108 ntype
->max_length
= max_length
;
1111 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1113 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1118 dict_remove(note_types
, ntype
->name
);
1122 static MODCMD_FUNC(cmd_removenote
) {
1123 struct note_type
*ntype
;
1126 ntype
= dict_find(note_types
, argv
[1], NULL
);
1127 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1130 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1137 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1140 chanserv_flush_note_type(ntype
);
1142 dict_remove(note_types
, argv
[1]);
1143 reply("CSMSG_NOTE_DELETED", argv
[1]);
1148 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1152 if(orig
->modes_set
& change
->modes_clear
)
1154 if(orig
->modes_clear
& change
->modes_set
)
1156 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1157 && strcmp(orig
->new_key
, change
->new_key
))
1159 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1160 && (orig
->new_limit
!= change
->new_limit
))
1165 static char max_length_text
[MAXLEN
+1][16];
1167 static struct helpfile_expansion
1168 chanserv_expand_variable(const char *variable
)
1170 struct helpfile_expansion exp
;
1172 if(!irccasecmp(variable
, "notes"))
1175 exp
.type
= HF_TABLE
;
1176 exp
.value
.table
.length
= 1;
1177 exp
.value
.table
.width
= 3;
1178 exp
.value
.table
.flags
= 0;
1179 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1180 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1181 exp
.value
.table
.contents
[0][0] = "Note Type";
1182 exp
.value
.table
.contents
[0][1] = "Visibility";
1183 exp
.value
.table
.contents
[0][2] = "Max Length";
1184 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1186 struct note_type
*ntype
= iter_data(it
);
1189 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1190 row
= exp
.value
.table
.length
++;
1191 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1192 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1193 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1194 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1196 if(!max_length_text
[ntype
->max_length
][0])
1197 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1198 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1203 exp
.type
= HF_STRING
;
1204 exp
.value
.str
= NULL
;
1208 static struct chanData
*
1209 register_channel(struct chanNode
*cNode
, char *registrar
)
1211 struct chanData
*channel
;
1212 enum levelOption lvlOpt
;
1213 enum charOption chOpt
;
1215 channel
= calloc(1, sizeof(struct chanData
));
1217 channel
->notes
= dict_new();
1218 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1220 channel
->registrar
= strdup(registrar
);
1221 channel
->registered
= now
;
1222 channel
->visited
= now
;
1223 channel
->limitAdjusted
= now
;
1224 channel
->ownerTransfer
= now
;
1225 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1226 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1227 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1228 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1229 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1231 channel
->prev
= NULL
;
1232 channel
->next
= channelList
;
1235 channelList
->prev
= channel
;
1236 channelList
= channel
;
1237 registered_channels
++;
1239 channel
->channel
= cNode
;
1241 cNode
->channel_info
= channel
;
1246 static struct userData
*
1247 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
, time_t accessexpiry
)
1249 struct userData
*ud
;
1251 if(access
> UL_OWNER
)
1254 ud
= calloc(1, sizeof(*ud
));
1255 ud
->channel
= channel
;
1256 ud
->handle
= handle
;
1258 ud
->access
= access
;
1259 ud
->info
= info
? strdup(info
) : NULL
;
1260 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1265 ud
->next
= channel
->users
;
1267 channel
->users
->prev
= ud
;
1268 channel
->users
= ud
;
1270 channel
->userCount
++;
1274 ud
->u_next
= ud
->handle
->channels
;
1276 ud
->u_next
->u_prev
= ud
;
1277 ud
->handle
->channels
= ud
;
1279 ud
->flags
= USER_FLAGS_DEFAULT
;
1283 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1286 chanserv_expire_tempuser(void *data
)
1288 struct userData
*uData
= data
;
1292 handle
= strdup(uData
->handle
->handle
);
1293 if (uData
->accessexpiry
> 0) {
1294 if (uData
->present
) {
1295 struct userNode
*user
, *next_un
= NULL
;
1296 struct handle_info
*hi
;
1298 hi
= get_handle_info(handle
);
1299 for (user
= hi
->users
; user
; user
= next_un
) {
1300 struct mod_chanmode
*change
;
1301 struct modeNode
*mn
;
1302 unsigned int count
= 0;
1304 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1305 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1306 next_un
= user
->next_authed
;
1310 change
= mod_chanmode_alloc(2);
1311 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1312 change
->args
[count
++].u
.member
= mn
;
1315 change
->argc
= count
;
1316 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1318 mod_chanmode_free(change
);
1319 next_un
= user
->next_authed
;
1322 del_channel_user(uData
, 1);
1328 chanserv_expire_tempclvl(void *data
)
1330 struct userData
*uData
= data
;
1334 handle
= strdup(uData
->handle
->handle
);
1335 if (uData
->clvlexpiry
> 0) {
1336 int changemodes
= 0;
1337 unsigned int mode
= 0;
1339 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1341 mode
= MODE_REMOVE
| MODE_CHANOP
;
1342 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1344 mode
= MODE_REMOVE
| MODE_HALFOP
;
1348 if (uData
->present
) {
1349 struct userNode
*user
, *next_un
= NULL
;
1350 struct handle_info
*hi
;
1352 hi
= get_handle_info(handle
);
1353 for (user
= hi
->users
; user
; user
= next_un
) {
1354 struct mod_chanmode
*change
;
1355 struct modeNode
*mn
;
1356 unsigned int count
= 0;
1358 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1359 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1360 next_un
= user
->next_authed
;
1364 if (changemodes
== 0) {
1365 next_un
= user
->next_authed
;
1369 change
= mod_chanmode_alloc(2);
1370 change
->args
[count
].mode
= mode
;
1371 change
->args
[count
++].u
.member
= mn
;
1374 change
->argc
= count
;
1375 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1377 mod_chanmode_free(change
);
1378 next_un
= user
->next_authed
;
1382 uData
->access
= uData
->lastaccess
;
1383 uData
->lastaccess
= 0;
1384 uData
->clvlexpiry
= 0;
1390 del_channel_user(struct userData
*user
, int do_gc
)
1392 struct chanData
*channel
= user
->channel
;
1394 channel
->userCount
--;
1397 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1398 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1401 user
->prev
->next
= user
->next
;
1403 channel
->users
= user
->next
;
1405 user
->next
->prev
= user
->prev
;
1408 user
->u_prev
->u_next
= user
->u_next
;
1410 user
->handle
->channels
= user
->u_next
;
1412 user
->u_next
->u_prev
= user
->u_prev
;
1416 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1417 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1418 unregister_channel(channel
, "lost all users.");
1422 static struct adduserPending
*
1423 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1425 struct adduserPending
*ap
;
1426 ap
= calloc(1,sizeof(struct adduserPending
));
1427 ap
->channel
= channel
;
1430 ap
->created
= time(NULL
);
1432 /* ap->prev defaults to NULL already.. */
1433 ap
->next
= adduser_pendings
;
1434 if(adduser_pendings
)
1435 adduser_pendings
->prev
= ap
;
1436 adduser_pendings
= ap
;
1437 adduser_pendings_count
++;
1442 del_adduser_pending(struct adduserPending
*ap
)
1445 ap
->prev
->next
= ap
->next
;
1447 adduser_pendings
= ap
->next
;
1450 ap
->next
->prev
= ap
->prev
;
1454 static void expire_adduser_pending();
1456 /* find_adduser_pending(channel, user) will find an arbitrary record
1457 * from user, channel, or user and channel.
1458 * if user or channel are NULL, they will match any records.
1460 static struct adduserPending
*
1461 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1463 struct adduserPending
*ap
;
1465 expire_adduser_pending(); /* why not here.. */
1467 if(!channel
&& !user
) /* 2 nulls matches all */
1468 return(adduser_pendings
);
1469 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1471 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1478 /* Remove all pendings for a user or channel
1480 * called in nickserv.c DelUser() and proto-* unregister_channel()
1483 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1485 struct adduserPending
*ap
;
1487 /* So this is a bit wastefull, i hate dealing with linked lists.
1488 * if its a problem we'll rewrite it right */
1489 while((ap
= find_adduser_pending(channel
, user
))) {
1490 del_adduser_pending(ap
);
1494 /* Called from nickserv.c cmd_auth after someone auths */
1496 process_adduser_pending(struct userNode
*user
)
1498 struct adduserPending
*ap
;
1499 if(!user
->handle_info
)
1500 return; /* not associated with an account */
1501 while((ap
= find_adduser_pending(NULL
, user
)))
1503 struct userData
*actee
;
1504 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1506 /* Already on the userlist. do nothing*/
1510 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1511 scan_user_presence(actee
, NULL
);
1513 del_adduser_pending(ap
);
1518 expire_adduser_pending()
1520 struct adduserPending
*ap
, *ap_next
;
1521 ap
= adduser_pendings
;
1524 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1526 ap_next
= ap
->next
; /* save next */
1527 del_adduser_pending(ap
); /* free and relink */
1528 ap
= ap_next
; /* advance */
1535 static void expire_ban(void *data
);
1538 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1541 unsigned int ii
, l1
, l2
;
1546 bd
= malloc(sizeof(struct banData
));
1548 bd
->channel
= channel
;
1550 bd
->triggered
= triggered
;
1551 bd
->expires
= expires
;
1553 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1555 extern const char *hidden_host_suffix
;
1556 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1560 l2
= strlen(old_name
);
1563 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1565 new_mask
= alloca(MAXLEN
);
1566 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1569 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1571 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1572 bd
->reason
= strdup(reason
);
1575 timeq_add(expires
, expire_ban
, bd
);
1578 bd
->next
= channel
->bans
; /* lamers */
1580 channel
->bans
->prev
= bd
;
1582 channel
->banCount
++;
1589 del_channel_ban(struct banData
*ban
)
1591 ban
->channel
->banCount
--;
1595 ban
->prev
->next
= ban
->next
;
1597 ban
->channel
->bans
= ban
->next
;
1600 ban
->next
->prev
= ban
->prev
;
1603 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1612 expire_ban(void *data
) /* lamer.. */
1614 struct banData
*bd
= data
;
1615 if(!IsSuspended(bd
->channel
))
1617 struct banList bans
;
1618 struct mod_chanmode change
;
1620 bans
= bd
->channel
->channel
->banlist
;
1621 mod_chanmode_init(&change
);
1622 for(ii
=0; ii
<bans
.used
; ii
++)
1624 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1627 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1628 change
.args
[0].u
.hostmask
= bd
->mask
;
1629 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1635 del_channel_ban(bd
);
1638 static void chanserv_expire_suspension(void *data
);
1641 unregister_channel(struct chanData
*channel
, const char *reason
)
1643 struct mod_chanmode change
;
1644 char msgbuf
[MAXLEN
];
1646 /* After channel unregistration, the following must be cleaned
1648 - Channel information.
1650 - Channel bans. (lamers)
1651 - Channel suspension data.
1652 - adduser_pending data.
1653 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1659 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1663 mod_chanmode_init(&change
);
1664 change
.modes_clear
|= MODE_REGISTERED
;
1665 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1668 wipe_adduser_pending(channel
->channel
, NULL
);
1670 while(channel
->users
)
1671 del_channel_user(channel
->users
, 0);
1673 while(channel
->bans
)
1674 del_channel_ban(channel
->bans
);
1676 free(channel
->topic
);
1677 free(channel
->registrar
);
1678 free(channel
->greeting
);
1679 free(channel
->user_greeting
);
1680 free(channel
->topic_mask
);
1683 channel
->prev
->next
= channel
->next
;
1685 channelList
= channel
->next
;
1688 channel
->next
->prev
= channel
->prev
;
1690 if(channel
->suspended
)
1692 struct chanNode
*cNode
= channel
->channel
;
1693 struct suspended
*suspended
, *next_suspended
;
1695 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1697 next_suspended
= suspended
->previous
;
1698 free(suspended
->suspender
);
1699 free(suspended
->reason
);
1700 if(suspended
->expires
)
1701 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1706 cNode
->channel_info
= NULL
;
1708 channel
->channel
->channel_info
= NULL
;
1710 dict_delete(channel
->notes
);
1711 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1712 if(!IsSuspended(channel
))
1713 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1714 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1715 UnlockChannel(channel
->channel
);
1717 registered_channels
--;
1721 expire_channels(UNUSED_ARG(void *data
))
1723 struct chanData
*channel
, *next
;
1724 struct userData
*user
;
1725 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1727 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1728 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1730 for(channel
= channelList
; channel
; channel
= next
)
1732 next
= channel
->next
;
1734 /* See if the channel can be expired. */
1735 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1736 || IsProtected(channel
))
1739 /* Make sure there are no high-ranking users still in the channel. */
1740 for(user
=channel
->users
; user
; user
=user
->next
)
1741 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1746 /* Unregister the channel */
1747 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1748 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1749 unregister_channel(channel
, "registration expired.");
1752 if(chanserv_conf
.channel_expire_frequency
)
1753 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1757 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1759 char protect
= channel
->chOpts
[chProtect
];
1760 struct userData
*cs_victim
, *cs_aggressor
;
1762 /* If victim access level is greater than set invitelevel, don't let
1763 * us kick them, but don't consider it punishment if someone else does
1767 if(victim
== aggressor
)
1769 /* Don't protect if the victim isn't authenticated (because they
1770 can't be a channel user), unless we are to protect non-users
1773 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1775 /* If they have enough access to invite themselvs through a ban,
1776 * and its us kicking them, don't. -Rubin */
1777 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1783 if(protect
!= 'a' && !cs_victim
)
1786 /* Protect if the aggressor isn't a user because at this point,
1787 the aggressor can only be less than or equal to the victim. */
1789 /* Not protected from chanserv except above */
1790 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1791 if(aggressor
== chanserv
)
1794 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1798 /* If the aggressor was a user, then the victim can't be helped. */
1805 if(cs_victim
->access
> cs_aggressor
->access
)
1810 if(cs_victim
->access
>= cs_aggressor
->access
)
1819 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1821 struct chanData
*cData
= channel
->channel_info
;
1822 struct userData
*cs_victim
;
1824 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1825 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1826 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1829 reply("CSMSG_OPBY_LOCKED");
1831 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1839 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1841 struct chanData
*cData
= channel
->channel_info
;
1842 struct userData
*cs_victim
;
1844 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1845 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1846 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1848 reply("CSMSG_HOPBY_LOCKED");
1857 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1859 if(IsService(victim
))
1861 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1865 if(protect_user(victim
, user
, channel
->channel_info
, false))
1867 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1875 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1877 if(IsService(victim
))
1879 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1883 if(protect_user(victim
, user
, channel
->channel_info
, false))
1885 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1892 static struct do_not_register
*
1893 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1895 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1896 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1897 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1898 strcpy(dnr
->reason
, reason
);
1900 if(dnr
->chan_name
[0] == '*')
1901 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1902 else if(strpbrk(dnr
->chan_name
, "*?"))
1903 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1905 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1909 static struct dnrList
1910 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1912 struct dnrList list
;
1914 struct do_not_register
*dnr
;
1916 dnrList_init(&list
);
1917 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1918 dnrList_append(&list
, dnr
);
1919 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1920 dnrList_append(&list
, dnr
);
1922 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1923 if(match_ircglob(chan_name
, iter_key(it
)))
1924 dnrList_append(&list
, iter_data(it
));
1929 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1931 struct dnrList list
;
1932 struct do_not_register
*dnr
;
1934 char buf
[INTERVALLEN
];
1936 list
= chanserv_find_dnrs(chan_name
, handle
);
1937 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1939 dnr
= list
.list
[ii
];
1942 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1943 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1946 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1949 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1954 struct do_not_register
*
1955 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1957 struct do_not_register
*dnr
;
1960 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1964 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1966 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1967 if(match_ircglob(chan_name
, iter_key(it
)))
1968 return iter_data(it
);
1973 static CHANSERV_FUNC(cmd_noregister
)
1976 struct do_not_register
*dnr
;
1977 char buf
[INTERVALLEN
];
1978 unsigned int matches
;
1984 reply("CSMSG_DNR_SEARCH_RESULTS");
1985 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1988 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1990 dnr
= iter_data(it
);
1992 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1994 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1997 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1999 dnr
= iter_data(it
);
2001 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2003 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2006 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
2008 dnr
= iter_data(it
);
2010 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2012 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2017 reply("MSG_MATCH_COUNT", matches
);
2019 reply("MSG_NO_MATCHES");
2025 if(!IsChannelName(target
) && (*target
!= '*'))
2027 reply("CSMSG_NOT_DNR", target
);
2033 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
2034 if((*target
== '*') && !get_handle_info(target
+ 1))
2036 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2039 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
2040 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2044 reply("CSMSG_DNR_SEARCH_RESULTS");
2045 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2048 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2050 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2052 reply("MSG_NO_MATCHES");
2056 static CHANSERV_FUNC(cmd_allowregister
)
2058 const char *chan_name
= argv
[1];
2060 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
2062 dict_remove(handle_dnrs
, chan_name
+1);
2063 reply("CSMSG_DNR_REMOVED", chan_name
);
2065 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
2067 dict_remove(plain_dnrs
, chan_name
);
2068 reply("CSMSG_DNR_REMOVED", chan_name
);
2070 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
2072 dict_remove(mask_dnrs
, chan_name
);
2073 reply("CSMSG_DNR_REMOVED", chan_name
);
2077 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2084 chanserv_get_owned_count(struct handle_info
*hi
)
2086 struct userData
*cList
;
2089 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2090 if(cList
->access
== UL_OWNER
)
2095 static CHANSERV_FUNC(cmd_register
)
2097 struct handle_info
*handle
;
2098 struct chanData
*cData
;
2099 struct modeNode
*mn
;
2100 char reason
[MAXLEN
];
2102 unsigned int new_channel
, force
=0;
2103 struct do_not_register
*dnr
;
2106 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2107 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2113 if(channel
->channel_info
)
2115 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2119 if(channel
->bad_channel
)
2121 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2125 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2127 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2132 chan_name
= channel
->name
;
2138 reply("MSG_MISSING_PARAMS", cmd
->name
);
2139 svccmd_send_help_brief(user
, chanserv
, cmd
);
2142 if(!IsChannelName(argv
[1]))
2144 reply("MSG_NOT_CHANNEL_NAME");
2148 if(opserv_bad_channel(argv
[1]))
2150 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2155 chan_name
= argv
[1];
2158 if(argc
>= (new_channel
+2))
2160 if(!IsHelping(user
))
2162 reply("CSMSG_PROXY_FORBIDDEN");
2166 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2168 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2169 dnr
= chanserv_is_dnr(chan_name
, handle
);
2171 /* Check if they are over the limit.. */
2172 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2174 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2181 handle
= user
->handle_info
;
2182 dnr
= chanserv_is_dnr(chan_name
, handle
);
2183 /* Check if they are over the limit.. */
2184 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2186 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2189 /* Check if another service is in the channel */
2191 for(n
= 0; n
< channel
->members
.used
; n
++)
2193 mn
= channel
->members
.list
[n
];
2194 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2196 reply("CSMSG_ANOTHER_SERVICE");
2203 if(!IsHelping(user
))
2204 reply("CSMSG_DNR_CHANNEL", chan_name
);
2206 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2210 /* now handled above for message specilization *
2211 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2213 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2219 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2221 cData
= register_channel(channel
, user
->handle_info
->handle
);
2222 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2223 cData
->modes
= chanserv_conf
.default_modes
;
2225 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2226 if (IsOffChannel(cData
))
2228 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2232 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2233 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2234 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2236 mod_chanmode_announce(chanserv
, channel
, change
);
2237 mod_chanmode_free(change
);
2240 /* Initialize the channel's max user record. */
2241 cData
->max
= channel
->members
.used
;
2243 if(handle
!= user
->handle_info
)
2244 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2247 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2248 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2249 handle
->handle
, user
->handle_info
->handle
);
2254 make_confirmation_string(struct userData
*uData
)
2256 static char strbuf
[16];
2261 for(src
= uData
->handle
->handle
; *src
; )
2262 accum
= accum
* 31 + toupper(*src
++);
2264 for(src
= uData
->channel
->channel
->name
; *src
; )
2265 accum
= accum
* 31 + toupper(*src
++);
2266 sprintf(strbuf
, "%08x", accum
);
2270 static CHANSERV_FUNC(cmd_unregister
)
2273 char reason
[MAXLEN
];
2274 struct chanData
*cData
;
2275 struct userData
*uData
;
2277 cData
= channel
->channel_info
;
2280 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2284 uData
= GetChannelUser(cData
, user
->handle_info
);
2285 if(!uData
|| (uData
->access
< UL_OWNER
))
2287 reply("CSMSG_NO_ACCESS");
2291 if(IsProtected(cData
))
2293 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2297 if(!IsHelping(user
))
2299 const char *confirm_string
;
2300 if(IsSuspended(cData
))
2302 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2305 confirm_string
= make_confirmation_string(uData
);
2306 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2308 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2313 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2314 name
= strdup(channel
->name
);
2315 unregister_channel(cData
, reason
);
2316 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2317 reply("CSMSG_UNREG_SUCCESS", name
);
2323 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2325 extern struct userNode
*spamserv
;
2326 struct mod_chanmode
*change
;
2328 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2330 change
= mod_chanmode_alloc(2);
2332 change
->args
[0].mode
= MODE_CHANOP
;
2333 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2334 change
->args
[1].mode
= MODE_CHANOP
;
2335 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2339 change
= mod_chanmode_alloc(1);
2341 change
->args
[0].mode
= MODE_CHANOP
;
2342 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2345 mod_chanmode_announce(chanserv
, channel
, change
);
2346 mod_chanmode_free(change
);
2349 static CHANSERV_FUNC(cmd_move
)
2351 struct mod_chanmode change
;
2352 struct chanNode
*target
;
2353 struct modeNode
*mn
;
2354 struct userData
*uData
;
2355 struct do_not_register
*dnr
;
2356 int chanserv_join
= 0, spamserv_join
;
2360 if(IsProtected(channel
->channel_info
))
2362 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2366 if(!IsChannelName(argv
[1]))
2368 reply("MSG_NOT_CHANNEL_NAME");
2372 if(opserv_bad_channel(argv
[1]))
2374 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2378 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2380 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2382 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2384 if(!IsHelping(user
))
2385 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2387 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2393 mod_chanmode_init(&change
);
2394 if(!(target
= GetChannel(argv
[1])))
2396 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2397 if(!IsSuspended(channel
->channel_info
))
2400 else if(target
->channel_info
)
2402 reply("CSMSG_ALREADY_REGGED", target
->name
);
2405 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2406 && !IsHelping(user
))
2408 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2411 else if(!IsSuspended(channel
->channel_info
))
2416 /* Clear MODE_REGISTERED from old channel, add it to new. */
2418 change
.modes_clear
= MODE_REGISTERED
;
2419 mod_chanmode_announce(chanserv
, channel
, &change
);
2420 change
.modes_clear
= 0;
2421 change
.modes_set
= MODE_REGISTERED
;
2422 mod_chanmode_announce(chanserv
, target
, &change
);
2425 /* Move the channel_info to the target channel; it
2426 shouldn't be necessary to clear timeq callbacks
2427 for the old channel. */
2428 target
->channel_info
= channel
->channel_info
;
2429 target
->channel_info
->channel
= target
;
2430 channel
->channel_info
= NULL
;
2432 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2435 ss_cs_join_channel(target
, spamserv_join
);
2437 if(!IsSuspended(target
->channel_info
))
2439 char reason2
[MAXLEN
];
2440 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2441 DelChannelUser(chanserv
, channel
, reason2
, 0);
2444 UnlockChannel(channel
);
2445 LockChannel(target
);
2446 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2447 channel
->name
, target
->name
, user
->handle_info
->handle
);
2449 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2454 merge_users(struct chanData
*source
, struct chanData
*target
)
2456 struct userData
*suData
, *tuData
, *next
;
2462 /* Insert the source's users into the scratch area. */
2463 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2464 dict_insert(merge
, suData
->handle
->handle
, suData
);
2466 /* Iterate through the target's users, looking for
2467 users common to both channels. The lower access is
2468 removed from either the scratch area or target user
2470 for(tuData
= target
->users
; tuData
; tuData
= next
)
2472 struct userData
*choice
;
2474 next
= tuData
->next
;
2476 /* If a source user exists with the same handle as a target
2477 channel's user, resolve the conflict by removing one. */
2478 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2482 /* Pick the data we want to keep. */
2483 /* If the access is the same, use the later seen time. */
2484 if(suData
->access
== tuData
->access
)
2485 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2486 else /* Otherwise, keep the higher access level. */
2487 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2489 /* Remove the user that wasn't picked. */
2490 if(choice
== tuData
)
2492 dict_remove(merge
, suData
->handle
->handle
);
2493 del_channel_user(suData
, 0);
2496 del_channel_user(tuData
, 0);
2499 /* Move the remaining users to the target channel. */
2500 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2502 suData
= iter_data(it
);
2504 /* Insert the user into the target channel's linked list. */
2505 suData
->prev
= NULL
;
2506 suData
->next
= target
->users
;
2507 suData
->channel
= target
;
2510 target
->users
->prev
= suData
;
2511 target
->users
= suData
;
2513 /* Update the user counts for the target channel; the
2514 source counts are left alone. */
2515 target
->userCount
++;
2518 /* Possible to assert (source->users == NULL) here. */
2519 source
->users
= NULL
;
2524 merge_bans(struct chanData
*source
, struct chanData
*target
)
2526 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2528 /* Hold on to the original head of the target ban list
2529 to avoid comparing source bans with source bans. */
2530 tFront
= target
->bans
;
2532 /* Perform a totally expensive O(n*m) merge, ick. */
2533 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2535 /* Flag to track whether the ban's been moved
2536 to the destination yet. */
2539 /* Possible to assert (sbData->prev == NULL) here. */
2540 sNext
= sbData
->next
;
2542 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2544 tNext
= tbData
->next
;
2546 /* Perform two comparisons between each source
2547 and target ban, conflicts are resolved by
2548 keeping the broader ban and copying the later
2549 expiration and triggered time. */
2550 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2552 /* There is a broader ban in the target channel that
2553 overrides one in the source channel; remove the
2554 source ban and break. */
2555 if(sbData
->expires
> tbData
->expires
)
2556 tbData
->expires
= sbData
->expires
;
2557 if(sbData
->triggered
> tbData
->triggered
)
2558 tbData
->triggered
= sbData
->triggered
;
2559 del_channel_ban(sbData
);
2562 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2564 /* There is a broader ban in the source channel that
2565 overrides one in the target channel; remove the
2566 target ban, fall through and move the source over. */
2567 if(tbData
->expires
> sbData
->expires
)
2568 sbData
->expires
= tbData
->expires
;
2569 if(tbData
->triggered
> sbData
->triggered
)
2570 sbData
->triggered
= tbData
->triggered
;
2571 if(tbData
== tFront
)
2573 del_channel_ban(tbData
);
2576 /* Source bans can override multiple target bans, so
2577 we allow a source to run through this loop multiple
2578 times, but we can only move it once. */
2583 /* Remove the source ban from the source ban list. */
2585 sbData
->next
->prev
= sbData
->prev
;
2587 /* Modify the source ban's associated channel. */
2588 sbData
->channel
= target
;
2590 /* Insert the ban into the target channel's linked list. */
2591 sbData
->prev
= NULL
;
2592 sbData
->next
= target
->bans
;
2595 target
->bans
->prev
= sbData
;
2596 target
->bans
= sbData
;
2598 /* Update the user counts for the target channel. */
2603 /* Possible to assert (source->bans == NULL) here. */
2604 source
->bans
= NULL
;
2608 merge_data(struct chanData
*source
, struct chanData
*target
)
2610 /* Use more recent visited and owner-transfer time; use older
2611 * registered time. Bitwise or may_opchan. Use higher max.
2612 * Do not touch last_refresh, ban count or user counts.
2614 if(source
->visited
> target
->visited
)
2615 target
->visited
= source
->visited
;
2616 if(source
->registered
< target
->registered
)
2617 target
->registered
= source
->registered
;
2618 if(source
->ownerTransfer
> target
->ownerTransfer
)
2619 target
->ownerTransfer
= source
->ownerTransfer
;
2620 if(source
->may_opchan
)
2621 target
->may_opchan
= 1;
2622 if(source
->max
> target
->max
)
2623 target
->max
= source
->max
;
2627 merge_channel(struct chanData
*source
, struct chanData
*target
)
2629 merge_users(source
, target
);
2630 merge_bans(source
, target
);
2631 merge_data(source
, target
);
2634 static CHANSERV_FUNC(cmd_merge
)
2636 struct userData
*target_user
;
2637 struct chanNode
*target
;
2638 char reason
[MAXLEN
];
2642 /* Make sure the target channel exists and is registered to the user
2643 performing the command. */
2644 if(!(target
= GetChannel(argv
[1])))
2646 reply("MSG_INVALID_CHANNEL");
2650 if(!target
->channel_info
)
2652 reply("CSMSG_NOT_REGISTERED", target
->name
);
2656 if(IsProtected(channel
->channel_info
))
2658 reply("CSMSG_MERGE_NODELETE");
2662 if(IsSuspended(target
->channel_info
))
2664 reply("CSMSG_MERGE_SUSPENDED");
2668 if(channel
== target
)
2670 reply("CSMSG_MERGE_SELF");
2674 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2675 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2677 reply("CSMSG_MERGE_NOT_OWNER");
2681 /* Merge the channel structures and associated data. */
2682 merge_channel(channel
->channel_info
, target
->channel_info
);
2683 spamserv_cs_move_merge(user
, channel
, target
, 0);
2684 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2685 unregister_channel(channel
->channel_info
, reason
);
2686 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2690 static CHANSERV_FUNC(cmd_opchan
)
2692 struct mod_chanmode change
;
2693 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2695 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2698 channel
->channel_info
->may_opchan
= 0;
2699 mod_chanmode_init(&change
);
2701 change
.args
[0].mode
= MODE_CHANOP
;
2702 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2703 mod_chanmode_announce(chanserv
, channel
, &change
);
2704 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2708 static CHANSERV_FUNC(cmd_adduser
)
2710 struct userData
*actee
;
2711 struct userData
*actor
;
2712 struct handle_info
*handle
;
2713 unsigned short access
;
2717 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2719 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2723 access
= user_level_from_name(argv
[2], UL_OWNER
);
2726 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2730 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2731 if(actor
->access
<= access
)
2733 reply("CSMSG_NO_BUMP_ACCESS");
2737 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2739 // 'kevin must first authenticate with AuthServ.' is sent to user
2740 struct userNode
*unode
;
2741 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2744 if(find_adduser_pending(channel
, unode
)) {
2745 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2748 if(IsInChannel(channel
, unode
)) {
2749 reply("CSMSG_ADDUSER_PENDING");
2750 add_adduser_pending(channel
, unode
, access
);
2751 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2753 /* this results in user must auth AND not in chan errors. too confusing..
2755 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2763 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2765 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2769 time_t accessexpiry
= 0;
2770 unsigned int duration
= 0;
2772 if ((duration
= ParseInterval(argv
[3])))
2773 accessexpiry
= now
+ duration
;
2776 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
, accessexpiry
);
2777 scan_user_presence(actee
, NULL
);
2780 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
2782 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2786 static CHANSERV_FUNC(cmd_clvl
)
2788 struct handle_info
*handle
;
2789 struct userData
*victim
;
2790 struct userData
*actor
;
2791 unsigned short new_access
;
2792 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2796 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2798 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2801 if(handle
== user
->handle_info
&& !privileged
)
2803 reply("CSMSG_NO_SELF_CLVL");
2807 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2809 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2813 if(actor
->access
<= victim
->access
&& !privileged
)
2815 reply("MSG_USER_OUTRANKED", handle
->handle
);
2819 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2823 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2827 if(new_access
>= actor
->access
&& !privileged
)
2829 reply("CSMSG_NO_BUMP_ACCESS");
2833 time_t clvlexpiry
= 0;
2834 unsigned int duration
= 0;
2836 if ((duration
= ParseInterval(argv
[3])))
2837 clvlexpiry
= now
+ duration
;
2841 if (victim
->accessexpiry
> 0) {
2842 reply("CSMSG_NO_BUMP_EXPIRY");
2846 victim
->clvlexpiry
= clvlexpiry
;
2847 victim
->lastaccess
= victim
->access
;
2848 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
2851 victim
->access
= new_access
;
2852 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2856 static CHANSERV_FUNC(cmd_deluser
)
2858 struct handle_info
*handle
;
2859 struct userData
*victim
;
2860 struct userData
*actor
;
2861 unsigned short access
;
2866 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2868 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2871 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2873 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2879 access
= user_level_from_name(argv
[1], UL_OWNER
);
2880 char *useraccess
= user_level_name_from_level(victim
->access
);
2883 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2886 if(strcasecmp(argv
[1], useraccess
))
2888 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2894 access
= victim
->access
;
2897 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2899 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2903 chan_name
= strdup(channel
->name
);
2904 del_channel_user(victim
, 1);
2905 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2911 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2913 struct userData
*actor
, *uData
, *next
;
2915 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2917 if(min_access
> max_access
)
2919 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2923 if((actor
->access
<= max_access
) && !IsHelping(user
))
2925 reply("CSMSG_NO_ACCESS");
2929 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2933 if((uData
->access
>= min_access
)
2934 && (uData
->access
<= max_access
)
2935 && match_ircglob(uData
->handle
->handle
, mask
))
2936 del_channel_user(uData
, 1);
2939 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2943 static CHANSERV_FUNC(cmd_mdelowner
)
2945 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2948 static CHANSERV_FUNC(cmd_mdelcoowner
)
2950 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
2953 static CHANSERV_FUNC(cmd_mdelmanager
)
2955 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
2958 static CHANSERV_FUNC(cmd_mdelop
)
2960 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
2963 static CHANSERV_FUNC(cmd_mdelhalfop
)
2965 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
2968 static CHANSERV_FUNC(cmd_mdelpeon
)
2970 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
2974 static CHANSERV_FUNC(cmd_levels
)
2976 struct helpfile_table tbl
;
2979 tbl
.length
= 6 + 1; // 6 levels
2982 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
2983 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
2984 tbl
.contents
[0][0] = "Level";
2985 tbl
.contents
[0][1] = "From";
2986 tbl
.contents
[0][2] = "-";
2987 tbl
.contents
[0][3] = "To";
2989 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2990 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
2991 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
2992 tbl
.contents
[ii
][2] = msnprintf(2, " ");
2993 tbl
.contents
[ii
][3] = msnprintf(1, "");
2995 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2996 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
2997 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
2998 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2999 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3001 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3002 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3003 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3004 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3005 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3007 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3008 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3009 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3010 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3011 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3013 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3014 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3015 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3016 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3017 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3019 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3020 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3021 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3022 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3023 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3025 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3029 reply("CSMSG_LEVELS_HEADER");
3030 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3031 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3032 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3033 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3034 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3035 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3042 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3044 struct banData
*bData
, *next
;
3045 char interval
[INTERVALLEN
];
3050 limit
= now
- duration
;
3051 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3055 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3058 del_channel_ban(bData
);
3062 intervalString(interval
, duration
, user
->handle_info
);
3063 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3068 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
)
3070 struct userData
*actor
, *uData
, *next
;
3071 char interval
[INTERVALLEN
];
3075 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3076 if(min_access
> max_access
)
3078 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3082 if((actor
->access
<= max_access
) && !IsHelping(user
))
3084 reply("CSMSG_NO_ACCESS");
3089 limit
= now
- duration
;
3090 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3094 if((uData
->seen
> limit
)
3096 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3099 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3100 || (!max_access
&& (uData
->access
< actor
->access
)))
3102 del_channel_user(uData
, 1);
3110 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3112 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3116 static CHANSERV_FUNC(cmd_trim
)
3118 unsigned long duration
;
3119 unsigned short min_level
, max_level
;
3124 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3125 duration
= ParseInterval(argv
[2]);
3128 reply("CSMSG_CANNOT_TRIM");
3132 if(!irccasecmp(argv
[1], "lamers"))
3134 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3137 else if(!irccasecmp(argv
[1], "users"))
3139 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3142 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3144 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3147 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3149 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3154 reply("CSMSG_INVALID_TRIM", argv
[1]);
3159 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3160 to the user. cmd_all takes advantage of this. */
3161 static CHANSERV_FUNC(cmd_up
)
3163 struct mod_chanmode change
;
3164 struct userData
*uData
;
3167 mod_chanmode_init(&change
);
3169 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3170 if(!change
.args
[0].u
.member
)
3173 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3177 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3181 reply("CSMSG_GODMODE_UP", argv
[0]);
3184 else if(uData
->access
>= UL_OP
)
3186 change
.args
[0].mode
= MODE_CHANOP
;
3187 errmsg
= "CSMSG_ALREADY_OPPED";
3189 else if(uData
->access
>= UL_HALFOP
)
3191 change
.args
[0].mode
= MODE_HALFOP
;
3192 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3194 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3196 change
.args
[0].mode
= MODE_VOICE
;
3197 errmsg
= "CSMSG_ALREADY_VOICED";
3202 reply("CSMSG_NO_ACCESS");
3205 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3206 if(!change
.args
[0].mode
)
3209 reply(errmsg
, channel
->name
);
3212 modcmd_chanmode_announce(&change
);
3216 static CHANSERV_FUNC(cmd_down
)
3218 struct mod_chanmode change
;
3220 mod_chanmode_init(&change
);
3222 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3223 if(!change
.args
[0].u
.member
)
3226 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3230 if(!change
.args
[0].u
.member
->modes
)
3233 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3237 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3238 modcmd_chanmode_announce(&change
);
3242 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
)
3244 struct userData
*cList
;
3246 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3248 if(IsSuspended(cList
->channel
)
3249 || IsUserSuspended(cList
)
3250 || !GetUserMode(cList
->channel
->channel
, user
))
3253 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3259 static CHANSERV_FUNC(cmd_upall
)
3261 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3264 static CHANSERV_FUNC(cmd_downall
)
3266 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3269 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3270 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3273 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
)
3275 unsigned int ii
, valid
;
3276 struct userNode
*victim
;
3277 struct mod_chanmode
*change
;
3279 change
= mod_chanmode_alloc(argc
- 1);
3281 for(ii
=valid
=0; ++ii
< argc
; )
3283 if(!(victim
= GetUserH(argv
[ii
])))
3285 change
->args
[valid
].mode
= mode
;
3286 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3287 if(!change
->args
[valid
].u
.member
)
3289 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3294 change
->argc
= valid
;
3295 if(valid
< (argc
-1))
3296 reply("CSMSG_PROCESS_FAILED");
3299 modcmd_chanmode_announce(change
);
3300 reply(action
, channel
->name
);
3302 mod_chanmode_free(change
);
3306 static CHANSERV_FUNC(cmd_op
)
3308 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3311 static CHANSERV_FUNC(cmd_hop
)
3313 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3316 static CHANSERV_FUNC(cmd_deop
)
3318 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3321 static CHANSERV_FUNC(cmd_dehop
)
3323 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3326 static CHANSERV_FUNC(cmd_voice
)
3328 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3331 static CHANSERV_FUNC(cmd_devoice
)
3333 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3337 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3343 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3345 struct modeNode
*mn
= channel
->members
.list
[ii
];
3347 if(IsService(mn
->user
))
3350 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3353 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3357 victims
[(*victimCount
)++] = mn
;
3363 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3365 struct userNode
*victim
;
3366 struct modeNode
**victims
;
3367 unsigned int offset
, n
, victimCount
, duration
= 0;
3368 char *reason
= "Bye.", *ban
, *name
;
3369 char interval
[INTERVALLEN
];
3371 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3372 REQUIRE_PARAMS(offset
);
3375 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3376 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3378 /* Truncate the reason to a length of TOPICLEN, as
3379 the ircd does; however, leave room for an ellipsis
3380 and the kicker's nick. */
3381 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3385 if((victim
= GetUserH(argv
[1])))
3387 victims
= alloca(sizeof(victims
[0]));
3388 victims
[0] = GetUserMode(channel
, victim
);
3389 /* XXX: The comparison with ACTION_KICK is just because all
3390 * other actions can work on users outside the channel, and we
3391 * want to allow those (e.g. unbans) in that case. If we add
3392 * some other ejection action for in-channel users, change
3394 victimCount
= victims
[0] ? 1 : 0;
3396 if(IsService(victim
))
3399 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3403 if((action
== ACTION_KICK
) && !victimCount
)
3406 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3410 if(protect_user(victim
, user
, channel
->channel_info
, false))
3412 // This translates to send_message(user, cmd->parent->bot, ...)
3413 // if user is x3 (ctcp action) cmd is null and segfault.
3415 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3419 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3420 name
= victim
->nick
;
3424 if(!is_ircmask(argv
[1]))
3427 reply("MSG_NICK_UNKNOWN", argv
[1]);
3431 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3433 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3436 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3439 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3440 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3442 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3443 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3444 some creativity, but its not x3's job to be the ban censor anyway. */
3445 if(is_overmask(argv
[1]))
3448 reply("CSMSG_LAME_MASK", argv
[1]);
3452 if((action
== ACTION_KICK
) && (victimCount
== 0))
3455 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3459 name
= ban
= strdup(argv
[1]);
3462 /* Truncate the ban in place if necessary; we must ensure
3463 that 'ban' is a valid ban mask before sanitizing it. */
3464 sanitize_ircmask(ban
);
3466 if(action
& ACTION_ADD_LAMER
)
3468 struct banData
*bData
, *next
;
3470 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3473 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3478 if(action
& ACTION_ADD_TIMED_LAMER
)
3480 duration
= ParseInterval(argv
[2]);
3485 reply("CSMSG_DURATION_TOO_LOW");
3489 else if(duration
> (86400 * 365 * 2))
3492 reply("CSMSG_DURATION_TOO_HIGH");
3499 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3501 if(match_ircglobs(bData
->mask
, ban
))
3503 int exact
= !irccasecmp(bData
->mask
, ban
);
3505 /* The ban is redundant; there is already a ban
3506 with the same effect in place. */
3510 free(bData
->reason
);
3511 bData
->reason
= strdup(reason
);
3512 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3514 reply("CSMSG_REASON_CHANGE", ban
);
3518 if(exact
&& bData
->expires
)
3522 /* If the ban matches an existing one exactly,
3523 extend the expiration time if the provided
3524 duration is longer. */
3525 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3527 bData
->expires
= now
+ duration
;
3538 /* Delete the expiration timeq entry and
3539 requeue if necessary. */
3540 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3543 timeq_add(bData
->expires
, expire_ban
, bData
);
3547 /* automated kickban, dont reply */
3550 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3552 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3558 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3565 if(match_ircglobs(ban
, bData
->mask
))
3567 /* The ban we are adding makes previously existing
3568 bans redundant; silently remove them. */
3569 del_channel_ban(bData
);
3573 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
);
3575 name
= ban
= strdup(bData
->mask
);
3579 /* WHAT DOES THIS DO?? -Rubin */
3580 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3582 extern const char *hidden_host_suffix
;
3583 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3585 unsigned int l1
, l2
;
3588 l2
= strlen(old_name
);
3591 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3593 new_mask
= malloc(MAXLEN
);
3594 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3596 name
= ban
= new_mask
;
3601 if(action
& ACTION_BAN
)
3603 unsigned int exists
;
3604 struct mod_chanmode
*change
;
3606 if(channel
->banlist
.used
>= MAXBANS
)
3609 reply("CSMSG_BANLIST_FULL", channel
->name
);
3614 exists
= ChannelBanExists(channel
, ban
);
3615 change
= mod_chanmode_alloc(victimCount
+ 1);
3616 for(n
= 0; n
< victimCount
; ++n
)
3618 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3619 change
->args
[n
].u
.member
= victims
[n
];
3623 change
->args
[n
].mode
= MODE_BAN
;
3624 change
->args
[n
++].u
.hostmask
= ban
;
3628 modcmd_chanmode_announce(change
);
3630 mod_chanmode_announce(chanserv
, channel
, change
);
3631 mod_chanmode_free(change
);
3633 if(exists
&& (action
== ACTION_BAN
))
3636 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3642 if(action
& ACTION_ADD_LAMER
)
3644 char kick_reason
[MAXLEN
];
3645 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3647 for(n
= 0; n
< victimCount
; n
++) {
3648 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3649 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3653 else if(action
& ACTION_KICK
)
3655 char kick_reason
[MAXLEN
];
3656 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3658 for(n
= 0; n
< victimCount
; n
++) {
3659 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3665 /* No response, since it was automated. */
3667 else if(action
& ACTION_ADD_LAMER
)
3670 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3672 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3674 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3675 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3676 else if(action
& ACTION_BAN
)
3677 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3678 else if(action
& ACTION_KICK
&& victimCount
)
3679 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3685 static CHANSERV_FUNC(cmd_kickban
)
3687 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3690 static CHANSERV_FUNC(cmd_kick
)
3692 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3695 static CHANSERV_FUNC(cmd_ban
)
3697 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3700 static CHANSERV_FUNC(cmd_addlamer
)
3702 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3705 static CHANSERV_FUNC(cmd_addtimedlamer
)
3707 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3710 static struct mod_chanmode
*
3711 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3713 struct mod_chanmode
*change
;
3714 unsigned char *match
;
3715 unsigned int ii
, count
;
3717 match
= alloca(bans
->used
);
3720 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3722 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3723 MATCH_USENICK
| MATCH_VISIBLE
);
3730 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3732 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3739 change
= mod_chanmode_alloc(count
);
3740 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3744 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3745 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3747 assert(count
== change
->argc
);
3751 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3753 unsigned int jj
, ii
, count
;
3755 struct chanData
*channel
;
3757 struct mod_chanmode
*change
;
3759 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3760 /* Walk through every channel */
3761 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3762 switch(channel
->chOpts
[chBanTimeout
])
3764 default: case '0': continue; /* Dont remove bans in this chan */
3765 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3766 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3767 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3768 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3769 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3772 /* First find out how many bans were going to unset */
3773 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3774 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3778 /* At least one ban, so setup a removal */
3779 change
= mod_chanmode_alloc(count
);
3781 /* Walk over every ban in this channel.. */
3782 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3783 bn
= channel
->channel
->banlist
.list
[jj
];
3784 if (bn
->set
< bantimeout
) {
3785 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3787 /* Add this ban to the mode change */
3788 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3789 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3791 /* Pull this ban out of the list */
3792 banList_remove(&(channel
->channel
->banlist
), bn
);
3797 /* Send the modes to IRC */
3798 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3800 /* free memory from strdup above */
3801 for(ii
= 0; ii
< count
; ++ii
)
3802 free((char*)change
->args
[ii
].u
.hostmask
);
3804 mod_chanmode_free(change
);
3807 /* Set this function to run again */
3808 if(chanserv_conf
.ban_timeout_frequency
)
3809 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3814 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3816 struct userNode
*actee
;
3822 /* may want to allow a comma delimited list of users... */
3823 if(!(actee
= GetUserH(argv
[1])))
3825 if(!is_ircmask(argv
[1]))
3827 reply("MSG_NICK_UNKNOWN", argv
[1]);
3831 mask
= strdup(argv
[1]);
3834 /* We don't sanitize the mask here because ircu
3836 if(action
& ACTION_UNBAN
)
3838 struct mod_chanmode
*change
;
3839 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3844 modcmd_chanmode_announce(change
);
3845 for(ii
= 0; ii
< change
->argc
; ++ii
)
3846 free((char*)change
->args
[ii
].u
.hostmask
);
3847 mod_chanmode_free(change
);
3852 if(action
& ACTION_DEL_LAMER
)
3854 struct banData
*ban
, *next
;
3856 ban
= channel
->channel_info
->bans
; /* lamers */
3860 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3863 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3868 del_channel_ban(ban
);
3875 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3877 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3883 static CHANSERV_FUNC(cmd_unban
)
3885 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3888 static CHANSERV_FUNC(cmd_dellamer
)
3890 /* it doesn't necessarily have to remove the channel ban - may want
3891 to make that an option. */
3892 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3895 static CHANSERV_FUNC(cmd_unbanme
)
3897 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3898 long flags
= ACTION_UNBAN
;
3900 /* remove permanent bans if the user has the proper access. */
3901 if(uData
->access
>= UL_MANAGER
)
3902 flags
|= ACTION_DEL_LAMER
;
3904 argv
[1] = user
->nick
;
3905 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3908 static CHANSERV_FUNC(cmd_unbanall
)
3910 struct mod_chanmode
*change
;
3913 if(!channel
->banlist
.used
)
3915 reply("CSMSG_NO_BANS", channel
->name
);
3919 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3920 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3922 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3923 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3925 modcmd_chanmode_announce(change
);
3926 for(ii
= 0; ii
< change
->argc
; ++ii
)
3927 free((char*)change
->args
[ii
].u
.hostmask
);
3928 mod_chanmode_free(change
);
3929 reply("CSMSG_BANS_REMOVED", channel
->name
);
3933 static CHANSERV_FUNC(cmd_open
)
3935 struct mod_chanmode
*change
;
3938 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3940 change
= mod_chanmode_alloc(0);
3941 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3942 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3943 && channel
->channel_info
->modes
.modes_set
)
3944 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3945 modcmd_chanmode_announce(change
);
3946 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3947 for(ii
= 0; ii
< change
->argc
; ++ii
)
3948 free((char*)change
->args
[ii
].u
.hostmask
);
3949 mod_chanmode_free(change
);
3953 static CHANSERV_FUNC(cmd_myaccess
)
3955 static struct string_buffer sbuf
;
3956 struct handle_info
*target_handle
;
3957 struct userData
*uData
;
3960 target_handle
= user
->handle_info
;
3961 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3963 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
3965 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3968 if(!target_handle
->channels
)
3970 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3974 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3975 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3977 struct chanData
*cData
= uData
->channel
;
3979 if(uData
->access
> UL_OWNER
)
3981 if(IsProtected(cData
)
3982 && (target_handle
!= user
->handle_info
)
3983 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3986 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3987 if(uData
->flags
== USER_AUTO_OP
)
3988 string_buffer_append(&sbuf
, ',');
3989 if(IsUserSuspended(uData
))
3990 string_buffer_append(&sbuf
, 's');
3991 if(IsUserAutoOp(uData
))
3993 if(uData
->access
>= UL_OP
)
3994 string_buffer_append(&sbuf
, 'o');
3995 else if(uData
->access
>= UL_HALFOP
)
3996 string_buffer_append(&sbuf
, 'h');
3997 else if(uData
->access
>= UL_PEON
)
3998 string_buffer_append(&sbuf
, 'v');
4000 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4001 string_buffer_append(&sbuf
, 'i');
4002 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4003 string_buffer_append(&sbuf
, 'j');
4005 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4007 string_buffer_append_string(&sbuf
, ")]");
4008 string_buffer_append(&sbuf
, '\0');
4009 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4015 static CHANSERV_FUNC(cmd_access
)
4017 struct userNode
*target
;
4018 struct handle_info
*target_handle
;
4019 struct userData
*uData
;
4021 char prefix
[MAXLEN
];
4026 target_handle
= target
->handle_info
;
4028 else if((target
= GetUserH(argv
[1])))
4030 target_handle
= target
->handle_info
;
4032 else if(argv
[1][0] == '*')
4034 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4036 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4042 reply("MSG_NICK_UNKNOWN", argv
[1]);
4046 assert(target
|| target_handle
);
4048 if(target
== chanserv
)
4050 reply("CSMSG_IS_CHANSERV");
4058 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4063 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4066 reply("MSG_AUTHENTICATE");
4072 const char *epithet
= NULL
, *type
= NULL
;
4075 epithet
= chanserv_conf
.irc_operator_epithet
;
4078 else if(IsNetworkHelper(target
))
4080 epithet
= chanserv_conf
.network_helper_epithet
;
4081 type
= "network helper";
4083 else if(IsSupportHelper(target
))
4085 epithet
= chanserv_conf
.support_helper_epithet
;
4086 type
= "support helper";
4090 if(target_handle
->epithet
)
4091 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4093 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4095 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4099 sprintf(prefix
, "%s", target_handle
->handle
);
4102 if(!channel
->channel_info
)
4104 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4108 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4109 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4110 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4112 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4113 /* To prevent possible information leaks, only show infolines
4114 * if the requestor is in the channel or it's their own
4116 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4118 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4120 /* Likewise, only say it's suspended if the user has active
4121 * access in that channel or it's their own entry. */
4122 if(IsUserSuspended(uData
)
4123 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4124 || (user
->handle_info
== uData
->handle
)))
4126 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4131 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4137 /* This is never used...
4139 zoot_list(struct listData *list)
4141 struct userData *uData;
4142 unsigned int start, curr, highest, lowest;
4143 struct helpfile_table tmp_table;
4144 const char **temp, *msg;
4146 if(list->table.length == 1)
4149 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);
4151 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));
4152 msg = user_find_message(list->user, "MSG_NONE");
4153 send_message_type(4, list->user, list->bot, " %s", msg);
4155 tmp_table.width = list->table.width;
4156 tmp_table.flags = list->table.flags;
4157 list->table.contents[0][0] = " ";
4158 highest = list->highest;
4159 if(list->lowest != 0)
4160 lowest = list->lowest;
4161 else if(highest < 100)
4164 lowest = highest - 100;
4165 for(start = curr = 1; curr < list->table.length; )
4167 uData = list->users[curr-1];
4168 list->table.contents[curr++][0] = " ";
4169 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4172 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);
4174 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));
4175 temp = list->table.contents[--start];
4176 list->table.contents[start] = list->table.contents[0];
4177 tmp_table.contents = list->table.contents + start;
4178 tmp_table.length = curr - start;
4179 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4180 list->table.contents[start] = temp;
4182 highest = lowest - 1;
4183 lowest = (highest < 100) ? 0 : (highest - 99);
4190 normal_list(struct listData
*list
)
4194 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
);
4196 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
));
4197 if(list
->table
.length
== 1)
4199 msg
= user_find_message(list
->user
, "MSG_NONE");
4200 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4203 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4206 /* if these need changed, uncomment and customize
4208 clean_list(struct listData *list)
4212 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);
4214 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));
4215 if(list->table.length == 1)
4217 msg = user_find_message(list->user, "MSG_NONE");
4218 send_message_type(4, list->user, list->bot, " %s", msg);
4221 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4225 advanced_list(struct listData *list)
4229 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);
4231 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));
4232 if(list->table.length == 1)
4234 msg = user_find_message(list->user, "MSG_NONE");
4235 send_message_type(4, list->user, list->bot, " %s", msg);
4238 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4242 classic_list(struct listData *list)
4246 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4248 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4249 if(list->table.length == 1)
4251 msg = user_find_message(list->user, "MSG_NONE");
4252 send_message_type(4, list->user, list->bot, " %s", msg);
4255 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4260 userData_access_comp(const void *arg_a
, const void *arg_b
)
4262 const struct userData
*a
= *(struct userData
**)arg_a
;
4263 const struct userData
*b
= *(struct userData
**)arg_b
;
4265 if(a
->access
!= b
->access
)
4266 res
= b
->access
- a
->access
;
4268 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4273 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4275 void (*send_list
)(struct listData
*);
4276 struct userData
*uData
;
4277 struct listData lData
;
4278 unsigned int matches
;
4284 lData
.bot
= cmd
->parent
->bot
;
4285 lData
.channel
= channel
;
4286 lData
.lowest
= lowest
;
4287 lData
.highest
= highest
;
4288 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4289 send_list
= normal_list
;
4290 /* What does the following line do exactly?? */
4291 /*(void)zoot_list; ** since it doesn't show user levels */
4294 if(user->handle_info)
4296 switch(user->handle_info->userlist_style)
4298 case HI_STYLE_CLEAN:
4299 send_list = clean_list;
4301 case HI_STYLE_ADVANCED:
4302 send_list = advanced_list;
4304 case HI_STYLE_CLASSIC:
4305 send_list = classic_list;
4307 case HI_STYLE_NORMAL:
4309 send_list = normal_list;
4314 send_list
= normal_list
;
4316 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4318 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4320 if((uData
->access
< lowest
)
4321 || (uData
->access
> highest
)
4322 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4324 lData
.users
[matches
++] = uData
;
4326 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4328 lData
.table
.length
= matches
+1;
4329 lData
.table
.flags
= TABLE_NO_FREE
;
4330 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4332 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4333 lData
.table
.width
= 6; /* with level = 6 */
4335 lData
.table
.width
= 5; /* without = 5 */
4336 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4337 lData
.table
.contents
[0] = ary
;
4338 if(user
->handle_info
) {
4339 switch(user
->handle_info
->userlist_style
) {
4340 case HI_STYLE_CLASSIC
:
4343 case HI_STYLE_ADVANCED
:
4344 ary
[i
++] = "Access";
4347 case HI_STYLE_CLEAN
:
4348 ary
[i
++] = "Access";
4350 case HI_STYLE_NORMAL
:
4352 ary
[i
++] = "Access";
4357 ary
[i
++] = "Access";
4359 ary
[i
++] = "Account";
4360 ary
[i
] = "Last Seen";
4362 ary
[i
++] = "Status";
4363 ary
[i
++] = "Expiry";
4364 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4366 struct userData
*uData
= lData
.users
[matches
-1];
4367 char seen
[INTERVALLEN
];
4370 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4371 lData
.table
.contents
[matches
] = ary
;
4372 if(user
->handle_info
) {
4373 switch(user
->handle_info
->userlist_style
) {
4374 case HI_STYLE_CLASSIC
:
4375 ary
[i
++] = strtab(uData
->access
);
4377 case HI_STYLE_ADVANCED
:
4378 ary
[i
++] = user_level_name_from_level(uData
->access
);
4379 ary
[i
++] = strtab(uData
->access
);
4381 case HI_STYLE_CLEAN
:
4382 ary
[i
++] = user_level_name_from_level(uData
->access
);
4384 case HI_STYLE_NORMAL
:
4386 ary
[i
++] = user_level_name_from_level(uData
->access
);
4391 ary
[i
++] = user_level_name_from_level(uData
->access
);
4393 ary
[i
++] = uData
->handle
->handle
;
4396 else if(!uData
->seen
)
4399 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4400 ary
[i
] = strdup(ary
[i
]);
4402 if(IsUserSuspended(uData
))
4403 ary
[i
++] = "Suspended";
4404 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4405 ary
[i
++] = "Vacation";
4407 ary
[i
++] = "Normal";
4409 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4410 char delay
[INTERVALLEN
];
4413 if (uData
->accessexpiry
> 0) {
4414 diff
= uData
->accessexpiry
- now
;
4415 intervalString(delay
, diff
, user
->handle_info
);
4417 diff
= uData
->clvlexpiry
- now
;
4418 intervalString(delay
, diff
, user
->handle_info
);
4426 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4428 /* Free strdup above */
4429 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4430 free(lData
.table
.contents
[matches
]);
4432 free(lData
.table
.contents
[0]);
4433 free(lData
.table
.contents
);
4437 /* Remove this now that debugging is over? or improve it for
4438 * users? Would it be better tied into USERS somehow? -Rubin */
4439 static CHANSERV_FUNC(cmd_pending
)
4441 struct adduserPending
*ap
;
4442 reply("CSMSG_ADDUSER_PENDING_HEADER");
4443 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4445 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4446 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4447 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4451 static CHANSERV_FUNC(cmd_users
)
4453 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4456 static CHANSERV_FUNC(cmd_wlist
)
4458 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4461 static CHANSERV_FUNC(cmd_clist
)
4463 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4466 static CHANSERV_FUNC(cmd_mlist
)
4468 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4471 static CHANSERV_FUNC(cmd_olist
)
4473 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4476 static CHANSERV_FUNC(cmd_hlist
)
4478 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4481 static CHANSERV_FUNC(cmd_plist
)
4483 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4486 static CHANSERV_FUNC(cmd_lamers
)
4488 struct helpfile_table tbl
;
4489 unsigned int matches
= 0, timed
= 0, ii
;
4490 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4491 const char *msg_never
, *triggered
, *expires
;
4492 struct banData
*ban
, **bans
; /* lamers */
4499 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4500 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4503 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4505 if(search
&& !match_ircglobs(search
, ban
->mask
))
4507 bans
[matches
++] = ban
;
4512 tbl
.length
= matches
+ 1;
4513 tbl
.width
= 4 + timed
;
4515 tbl
.flags
= TABLE_NO_FREE
;
4516 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4517 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4518 tbl
.contents
[0][0] = "Mask";
4519 tbl
.contents
[0][1] = "Set By";
4520 tbl
.contents
[0][2] = "Triggered";
4523 tbl
.contents
[0][3] = "Expires";
4524 tbl
.contents
[0][4] = "Reason";
4527 tbl
.contents
[0][3] = "Reason";
4530 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4531 /* reply("MSG_NONE"); */
4532 free(tbl
.contents
[0]);
4537 msg_never
= user_find_message(user
, "MSG_NEVER");
4538 for(ii
= 0; ii
< matches
; )
4544 else if(ban
->expires
)
4545 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4547 expires
= msg_never
;
4550 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4552 triggered
= msg_never
;
4554 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4555 tbl
.contents
[ii
][0] = ban
->mask
;
4556 tbl
.contents
[ii
][1] = ban
->owner
;
4557 tbl
.contents
[ii
][2] = strdup(triggered
);
4560 tbl
.contents
[ii
][3] = strdup(expires
);
4561 tbl
.contents
[ii
][4] = ban
->reason
;
4564 tbl
.contents
[ii
][3] = ban
->reason
;
4566 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4567 /* reply("MSG_MATCH_COUNT", matches); */
4568 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4570 free((char*)tbl
.contents
[ii
][2]);
4572 free((char*)tbl
.contents
[ii
][3]);
4573 free(tbl
.contents
[ii
]);
4575 free(tbl
.contents
[0]);
4582 * return + if the user does NOT have the right to set the topic, and
4583 * the topic is changed.
4586 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4588 struct chanData
*cData
= channel
->channel_info
;
4589 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4591 else if(cData
->topic
)
4592 return irccasecmp(new_topic
, cData
->topic
);
4599 * Makes a givin topic fit into a givin topic mask and returns
4602 * topic_mask - the mask to conform to
4603 * topic - the topic to make conform
4604 * new_topic - the pre-allocated char* to put the new topic into
4606 * modifies: new_topic
4609 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4611 //char *topic_mask = cData->topic_mask;
4613 int pos
=0, starpos
=-1, dpos
=0, len
;
4615 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4622 strcpy(new_topic
, "");
4625 len
= strlen(topic
);
4626 if((dpos
+ len
) > TOPICLEN
)
4627 len
= TOPICLEN
+ 1 - dpos
;
4628 memcpy(new_topic
+dpos
, topic
, len
);
4632 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4633 default: new_topic
[dpos
++] = tchar
; break;
4636 if((dpos
> TOPICLEN
) || tchar
)
4638 strcpy(new_topic
, "");
4641 new_topic
[dpos
] = 0;
4645 static CHANSERV_FUNC(cmd_topic
)
4647 struct chanData
*cData
;
4651 #ifdef WITH_PROTOCOL_P10
4655 cData
= channel
->channel_info
;
4660 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4661 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4662 reply("CSMSG_TOPIC_SET", cData
->topic
);
4666 reply("CSMSG_NO_TOPIC", channel
->name
);
4670 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4671 /* If they say "!topic *", use an empty topic. */
4672 if((topic
[0] == '*') && (topic
[1] == 0))
4675 if(bad_topic(channel
, user
, topic
))
4677 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4682 /* If there is a topicmask set, and the new topic doesnt match, make it */
4683 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4685 char *topic_mask
= cData
->topic_mask
;
4686 char new_topic
[TOPICLEN
+1];
4688 /* make a new topic fitting mask */
4689 conform_topic(topic_mask
, topic
, new_topic
);
4692 /* Topic couldnt fit into mask, was too long */
4693 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4694 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4697 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4699 else /* No mask set, just set the topic */
4700 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4703 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4705 /* Grab the topic and save it as the default topic. */
4707 cData
->topic
= strdup(channel
->topic
);
4713 static CHANSERV_FUNC(cmd_mode
)
4715 struct userData
*uData
;
4716 struct mod_chanmode
*change
;
4721 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4722 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4726 change
= &channel
->channel_info
->modes
;
4727 if(change
->modes_set
|| change
->modes_clear
) {
4728 modcmd_chanmode_announce(change
);
4729 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4731 reply("CSMSG_NO_MODES", channel
->name
);
4735 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4737 base_oplevel
= MAXOPLEVEL
;
4738 else if (uData
->access
>= UL_OWNER
)
4741 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4742 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4746 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4750 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4751 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4754 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4755 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4759 modcmd_chanmode_announce(change
);
4760 mod_chanmode_free(change
);
4761 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4765 static CHANSERV_FUNC(cmd_invite
)
4767 struct userData
*uData
;
4768 struct userNode
*invite
;
4770 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4774 if(!(invite
= GetUserH(argv
[1])))
4776 reply("MSG_NICK_UNKNOWN", argv
[1]);
4783 if(GetUserMode(channel
, invite
))
4785 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4793 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4794 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4797 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4800 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4802 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4803 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4804 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4810 irc_invite(chanserv
, invite
, channel
);
4812 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4817 static CHANSERV_FUNC(cmd_inviteme
)
4819 if(GetUserMode(channel
, user
))
4821 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4824 if(channel
->channel_info
4825 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4827 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4830 irc_invite(cmd
->parent
->bot
, user
, channel
);
4835 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4838 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4840 /* We display things based on two dimensions:
4841 * - Issue time: present or absent
4842 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4843 * (in order of precedence, so something both expired and revoked
4844 * only counts as revoked)
4846 combo
= (suspended
->issued
? 4 : 0)
4847 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4849 case 0: /* no issue time, indefinite expiration */
4850 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4852 case 1: /* no issue time, expires in future */
4853 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4854 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4856 case 2: /* no issue time, expired */
4857 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4858 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4860 case 3: /* no issue time, revoked */
4861 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4862 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4864 case 4: /* issue time set, indefinite expiration */
4865 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4866 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4868 case 5: /* issue time set, expires in future */
4869 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4870 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4871 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4873 case 6: /* issue time set, expired */
4874 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4875 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4876 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4878 case 7: /* issue time set, revoked */
4879 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4880 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4881 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4884 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4890 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4893 const char *fmt
= "%a %b %d %H:%M %Y";
4894 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4896 if(giveownership
->staff_issuer
)
4898 if(giveownership
->reason
)
4899 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4900 giveownership
->target
, giveownership
->target_access
,
4901 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4903 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4904 giveownership
->target
, giveownership
->target_access
,
4905 giveownership
->staff_issuer
, buf
);
4909 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4914 static CHANSERV_FUNC(cmd_info
)
4916 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4917 struct userData
*uData
, *owner
;
4918 struct chanData
*cData
;
4919 struct do_not_register
*dnr
;
4924 cData
= channel
->channel_info
;
4925 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4926 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4929 uData
= GetChannelUser(cData
, user
->handle_info
);
4930 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4932 mod_chanmode_format(&cData
->modes
, modes
);
4933 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4934 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4937 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4941 note
= iter_data(it
);
4942 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4945 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4946 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4949 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4950 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4951 if(owner
->access
== UL_OWNER
)
4952 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4953 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4954 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4955 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4957 privileged
= IsStaff(user
);
4958 /* if(privileged) */
4959 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4960 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4961 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4963 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4964 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4966 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4968 struct suspended
*suspended
;
4969 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4970 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4971 show_suspension_info(cmd
, user
, suspended
);
4973 else if(IsSuspended(cData
))
4975 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4976 show_suspension_info(cmd
, user
, cData
->suspended
);
4978 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4980 struct giveownership
*giveownership
;
4981 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4982 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4983 show_giveownership_info(cmd
, user
, giveownership
);
4985 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4986 reply("CSMSG_CHANNEL_END");
4988 reply("CSMSG_CHANNEL_END_CLEAN");
4992 static CHANSERV_FUNC(cmd_netinfo
)
4994 extern time_t boot_time
;
4995 extern unsigned long burst_length
;
4996 char interval
[INTERVALLEN
];
4998 reply("CSMSG_NETWORK_INFO");
4999 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5000 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5001 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5002 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5003 reply("CSMSG_NETWORK_LAMERS", banCount
);
5004 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5005 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5006 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5011 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5013 struct helpfile_table table
;
5015 struct userNode
*user
;
5020 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5021 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5022 for(nn
=0; nn
<list
->used
; nn
++)
5024 user
= list
->list
[nn
];
5025 if(user
->modes
& skip_flags
)
5029 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5032 nick
= alloca(strlen(user
->nick
)+3);
5033 sprintf(nick
, "(%s)", user
->nick
);
5037 table
.contents
[table
.length
][0] = nick
;
5040 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5043 static CHANSERV_FUNC(cmd_ircops
)
5045 reply("CSMSG_STAFF_OPERS");
5046 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5050 static CHANSERV_FUNC(cmd_helpers
)
5052 reply("CSMSG_STAFF_HELPERS");
5053 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5057 static CHANSERV_FUNC(cmd_staff
)
5059 reply("CSMSG_NETWORK_STAFF");
5060 cmd_ircops(CSFUNC_ARGS
);
5061 cmd_helpers(CSFUNC_ARGS
);
5065 static CHANSERV_FUNC(cmd_peek
)
5067 struct modeNode
*mn
;
5068 char modes
[MODELEN
];
5070 struct helpfile_table table
;
5072 irc_make_chanmode(channel
, modes
);
5074 reply("CSMSG_PEEK_INFO", channel
->name
);
5075 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5077 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5078 reply("CSMSG_PEEK_MODES", modes
);
5079 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5083 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5084 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5085 for(n
= 0; n
< channel
->members
.used
; n
++)
5087 mn
= channel
->members
.list
[n
];
5088 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5090 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5091 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5096 reply("CSMSG_PEEK_OPS");
5097 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5100 reply("CSMSG_PEEK_NO_OPS");
5101 reply("CSMSG_PEEK_END");
5105 static MODCMD_FUNC(cmd_wipeinfo
)
5107 struct handle_info
*victim
;
5108 struct userData
*ud
, *actor
;
5111 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5112 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5114 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5116 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5119 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5121 reply("MSG_USER_OUTRANKED", victim
->handle
);
5127 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5132 resync_channel(struct chanNode
*channel
)
5134 struct mod_chanmode
*changes
;
5135 struct chanData
*cData
= channel
->channel_info
;
5136 unsigned int ii
, used
;
5138 /* 6 = worst case -ovh+ovh on everyone */
5139 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5140 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5142 struct modeNode
*mn
= channel
->members
.list
[ii
];
5143 struct userData
*uData
;
5145 if(IsService(mn
->user
))
5149 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5151 /* If the channel is in no-mode mode, de-mode EVERYONE */
5152 if(cData
->chOpts
[chAutomode
] == 'n')
5156 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5157 changes
->args
[used
++].u
.member
= mn
;
5160 else /* Give various userlevels their modes.. */
5162 if(uData
&& uData
->access
>= UL_OP
)
5164 if(!(mn
->modes
& MODE_CHANOP
))
5166 changes
->args
[used
].mode
= MODE_CHANOP
;
5167 changes
->args
[used
++].u
.member
= mn
;
5170 else if(uData
&& uData
->access
>= UL_HALFOP
)
5172 if(mn
->modes
& MODE_CHANOP
)
5174 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5175 changes
->args
[used
++].u
.member
= mn
;
5177 if(!(mn
->modes
& MODE_HALFOP
))
5179 changes
->args
[used
].mode
= MODE_HALFOP
;
5180 changes
->args
[used
++].u
.member
= mn
;
5183 else if(uData
&& uData
->access
>= UL_PEON
)
5185 if(mn
->modes
& MODE_CHANOP
)
5187 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5188 changes
->args
[used
++].u
.member
= mn
;
5190 if(mn
->modes
& MODE_HALFOP
)
5192 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5193 changes
->args
[used
++].u
.member
= mn
;
5195 /* Don't voice peons if were in mode m */
5196 if( cData
->chOpts
[chAutomode
] == 'm')
5198 if(mn
->modes
& MODE_VOICE
)
5200 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5201 changes
->args
[used
++].u
.member
= mn
;
5204 /* otherwise, make user they do have voice */
5205 else if(!(mn
->modes
& MODE_VOICE
))
5207 changes
->args
[used
].mode
= MODE_VOICE
;
5208 changes
->args
[used
++].u
.member
= mn
;
5211 else /* They arnt on the userlist.. */
5213 /* If we voice everyone, but they dont.. */
5214 if(cData
->chOpts
[chAutomode
] == 'v')
5216 /* Remove anything except v */
5217 if(mn
->modes
& ~MODE_VOICE
)
5219 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5220 changes
->args
[used
++].u
.member
= mn
;
5223 if(!(mn
->modes
& MODE_VOICE
))
5225 changes
->args
[used
].mode
= MODE_VOICE
;
5226 changes
->args
[used
++].u
.member
= mn
;
5229 /* If we hop everyone, but they dont.. */
5230 else if(cData
->chOpts
[chAutomode
] == 'h')
5232 /* Remove anything except h */
5233 if(mn
->modes
& ~MODE_HALFOP
)
5235 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5236 changes
->args
[used
++].u
.member
= mn
;
5239 if(!(mn
->modes
& MODE_HALFOP
))
5241 changes
->args
[used
].mode
= MODE_HALFOP
;
5242 changes
->args
[used
++].u
.member
= mn
;
5245 /* If we op everyone, but they dont.. */
5246 else if(cData
->chOpts
[chAutomode
] == 'o')
5248 /* Remove anything except h */
5249 if(mn
->modes
& ~MODE_CHANOP
)
5251 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5252 changes
->args
[used
++].u
.member
= mn
;
5255 if(!(mn
->modes
& MODE_CHANOP
))
5257 changes
->args
[used
].mode
= MODE_CHANOP
;
5258 changes
->args
[used
++].u
.member
= mn
;
5261 /* they have no excuse for having modes, de-everything them */
5266 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5267 changes
->args
[used
++].u
.member
= mn
;
5273 changes
->argc
= used
;
5274 mod_chanmode_announce(chanserv
, channel
, changes
);
5275 mod_chanmode_free(changes
);
5278 static CHANSERV_FUNC(cmd_resync
)
5280 resync_channel(channel
);
5281 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5285 static CHANSERV_FUNC(cmd_seen
)
5287 struct userData
*uData
;
5288 struct handle_info
*handle
;
5289 char seen
[INTERVALLEN
];
5293 if(!irccasecmp(argv
[1], chanserv
->nick
))
5295 reply("CSMSG_IS_CHANSERV");
5299 if(!(handle
= get_handle_info(argv
[1])))
5301 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5305 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5307 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5312 reply("CSMSG_USER_PRESENT", handle
->handle
);
5313 else if(uData
->seen
)
5314 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5316 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5318 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5319 reply("CSMSG_USER_VACATION", handle
->handle
);
5324 static MODCMD_FUNC(cmd_names
)
5326 struct userNode
*targ
;
5327 struct userData
*targData
;
5328 unsigned int ii
, pos
;
5331 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5333 targ
= channel
->members
.list
[ii
]->user
;
5334 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5337 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5340 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5344 if(IsUserSuspended(targData
))
5346 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5349 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5350 reply("CSMSG_END_NAMES", channel
->name
);
5355 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5357 switch(ntype
->visible_type
)
5359 case NOTE_VIS_ALL
: return 1;
5360 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5361 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5366 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5368 struct userData
*uData
;
5370 switch(ntype
->set_access_type
)
5372 case NOTE_SET_CHANNEL_ACCESS
:
5373 if(!user
->handle_info
)
5375 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5377 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5378 case NOTE_SET_CHANNEL_SETTER
:
5379 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5380 case NOTE_SET_PRIVILEGED
: default:
5381 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5385 static CHANSERV_FUNC(cmd_note
)
5387 struct chanData
*cData
;
5389 struct note_type
*ntype
;
5391 cData
= channel
->channel_info
;
5394 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5398 /* If no arguments, show all visible notes for the channel. */
5404 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5406 note
= iter_data(it
);
5407 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5410 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5411 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5414 reply("CSMSG_NOTELIST_END", channel
->name
);
5416 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5418 /* If one argument, show the named note. */
5421 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5422 && note_type_visible_to_user(cData
, note
->type
, user
))
5424 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5426 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5427 && note_type_visible_to_user(NULL
, ntype
, user
))
5429 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5434 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5438 /* Assume they're trying to set a note. */
5442 ntype
= dict_find(note_types
, argv
[1], NULL
);
5445 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5448 else if(note_type_settable_by_user(channel
, ntype
, user
))
5450 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5451 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5452 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5453 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5454 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5456 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5458 /* The note is viewable to staff only, so return 0
5459 to keep the invocation from getting logged (or
5460 regular users can see it in !events). */
5466 reply("CSMSG_NO_ACCESS");
5473 static CHANSERV_FUNC(cmd_delnote
)
5478 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5479 || !note_type_settable_by_user(channel
, note
->type
, user
))
5481 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5484 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5485 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5489 static CHANSERV_FUNC(cmd_last
)
5495 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5497 if(numoflines
< 1 || numoflines
> 200)
5499 reply("CSMSG_LAST_INVALID");
5502 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5506 static CHANSERV_FUNC(cmd_events
)
5508 struct logSearch discrim
;
5509 struct logReport report
;
5510 unsigned int matches
, limit
;
5512 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5513 if(limit
< 1 || limit
> 200)
5516 memset(&discrim
, 0, sizeof(discrim
));
5517 discrim
.masks
.bot
= chanserv
;
5518 discrim
.masks
.channel_name
= channel
->name
;
5520 discrim
.masks
.command
= argv
[2];
5521 discrim
.limit
= limit
;
5522 discrim
.max_time
= INT_MAX
;
5523 discrim
.severities
= 1 << LOG_COMMAND
;
5524 report
.reporter
= chanserv
;
5526 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5527 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5529 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5531 reply("MSG_MATCH_COUNT", matches
);
5533 reply("MSG_NO_MATCHES");
5537 static CHANSERV_FUNC(cmd_say
)
5543 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5544 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5546 else if(GetUserH(argv
[1]))
5549 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5550 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5554 reply("MSG_NOT_TARGET_NAME");
5560 static CHANSERV_FUNC(cmd_emote
)
5566 /* CTCP is so annoying. */
5567 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5568 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5570 else if(GetUserH(argv
[1]))
5572 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5573 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5577 reply("MSG_NOT_TARGET_NAME");
5583 struct channelList
*
5584 chanserv_support_channels(void)
5586 return &chanserv_conf
.support_channels
;
5589 static CHANSERV_FUNC(cmd_expire
)
5591 int channel_count
= registered_channels
;
5592 expire_channels(NULL
);
5593 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5598 chanserv_expire_suspension(void *data
)
5600 struct suspended
*suspended
= data
;
5601 struct chanNode
*channel
;
5603 if(!suspended
->expires
|| (now
< suspended
->expires
))
5604 suspended
->revoked
= now
;
5605 channel
= suspended
->cData
->channel
;
5606 suspended
->cData
->channel
= channel
;
5607 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5608 if(!IsOffChannel(suspended
->cData
))
5610 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5611 ss_cs_join_channel(channel
, 1);
5615 static CHANSERV_FUNC(cmd_csuspend
)
5617 struct suspended
*suspended
;
5618 char reason
[MAXLEN
];
5619 time_t expiry
, duration
;
5620 struct userData
*uData
;
5624 if(IsProtected(channel
->channel_info
))
5626 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5630 if(argv
[1][0] == '!')
5632 else if(IsSuspended(channel
->channel_info
))
5634 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5635 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5639 if(!strcmp(argv
[1], "0"))
5641 else if((duration
= ParseInterval(argv
[1])))
5642 expiry
= now
+ duration
;
5645 reply("MSG_INVALID_DURATION", argv
[1]);
5649 unsplit_string(argv
+ 2, argc
- 2, reason
);
5651 suspended
= calloc(1, sizeof(*suspended
));
5652 suspended
->revoked
= 0;
5653 suspended
->issued
= now
;
5654 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5655 suspended
->expires
= expiry
;
5656 suspended
->reason
= strdup(reason
);
5657 suspended
->cData
= channel
->channel_info
;
5658 suspended
->previous
= suspended
->cData
->suspended
;
5659 suspended
->cData
->suspended
= suspended
;
5661 if(suspended
->expires
)
5662 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5664 if(IsSuspended(channel
->channel_info
))
5666 suspended
->previous
->revoked
= now
;
5667 if(suspended
->previous
->expires
)
5668 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5670 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5671 channel
->name
, suspended
->suspender
);
5675 /* Mark all users in channel as absent. */
5676 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5685 /* Mark the channel as suspended, then part. */
5686 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5687 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5688 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5689 reply("CSMSG_SUSPENDED", channel
->name
);
5690 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5691 channel
->name
, suspended
->suspender
);
5696 static CHANSERV_FUNC(cmd_cunsuspend
)
5698 struct suspended
*suspended
;
5700 if(!IsSuspended(channel
->channel_info
))
5702 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5706 suspended
= channel
->channel_info
->suspended
;
5708 /* Expire the suspension and join ChanServ to the channel. */
5709 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5710 chanserv_expire_suspension(suspended
);
5711 reply("CSMSG_UNSUSPENDED", channel
->name
);
5712 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5713 channel
->name
, user
->handle_info
->handle
);
5717 typedef struct chanservSearch
5725 unsigned long flags
;
5729 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5732 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5737 search
= malloc(sizeof(struct chanservSearch
));
5738 memset(search
, 0, sizeof(*search
));
5741 for(i
= 0; i
< argc
; i
++)
5743 /* Assume all criteria require arguments. */
5746 reply("MSG_MISSING_PARAMS", argv
[i
]);
5750 if(!irccasecmp(argv
[i
], "name"))
5751 search
->name
= argv
[++i
];
5752 else if(!irccasecmp(argv
[i
], "registrar"))
5753 search
->registrar
= argv
[++i
];
5754 else if(!irccasecmp(argv
[i
], "unvisited"))
5755 search
->unvisited
= ParseInterval(argv
[++i
]);
5756 else if(!irccasecmp(argv
[i
], "registered"))
5757 search
->registered
= ParseInterval(argv
[++i
]);
5758 else if(!irccasecmp(argv
[i
], "flags"))
5761 if(!irccasecmp(argv
[i
], "nodelete"))
5762 search
->flags
|= CHANNEL_NODELETE
;
5763 else if(!irccasecmp(argv
[i
], "suspended"))
5764 search
->flags
|= CHANNEL_SUSPENDED
;
5767 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5771 else if(!irccasecmp(argv
[i
], "limit"))
5772 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5775 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5780 if(search
->name
&& !strcmp(search
->name
, "*"))
5782 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5783 search
->registrar
= 0;
5792 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5794 const char *name
= channel
->channel
->name
;
5795 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5796 (search
->registrar
&& !channel
->registrar
) ||
5797 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5798 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5799 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5800 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5807 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5809 struct chanData
*channel
;
5810 unsigned int matches
= 0;
5812 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5814 if(!chanserv_channel_match(channel
, search
))
5824 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5829 search_print(struct chanData
*channel
, void *data
)
5831 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5834 static CHANSERV_FUNC(cmd_search
)
5837 unsigned int matches
;
5838 channel_search_func action
;
5842 if(!irccasecmp(argv
[1], "count"))
5843 action
= search_count
;
5844 else if(!irccasecmp(argv
[1], "print"))
5845 action
= search_print
;
5848 reply("CSMSG_ACTION_INVALID", argv
[1]);
5852 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5856 if(action
== search_count
)
5857 search
->limit
= INT_MAX
;
5859 if(action
== search_print
)
5861 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5862 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5866 matches
= chanserv_channel_search(search
, action
, user
);
5869 reply("MSG_MATCH_COUNT", matches
);
5871 reply("MSG_NO_MATCHES");
5877 static CHANSERV_FUNC(cmd_unvisited
)
5879 struct chanData
*cData
;
5880 time_t interval
= chanserv_conf
.channel_expire_delay
;
5881 char buffer
[INTERVALLEN
];
5882 unsigned int limit
= 25, matches
= 0;
5886 interval
= ParseInterval(argv
[1]);
5888 limit
= atoi(argv
[2]);
5891 intervalString(buffer
, interval
, user
->handle_info
);
5892 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5894 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5896 if((now
- cData
->visited
) < interval
)
5899 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5900 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5907 static MODCMD_FUNC(chan_opt_defaulttopic
)
5913 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5915 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5919 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5921 free(channel
->channel_info
->topic
);
5922 if(topic
[0] == '*' && topic
[1] == 0)
5924 topic
= channel
->channel_info
->topic
= NULL
;
5928 topic
= channel
->channel_info
->topic
= strdup(topic
);
5929 if(channel
->channel_info
->topic_mask
5930 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5931 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5933 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5936 if(channel
->channel_info
->topic
)
5937 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5939 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5943 static MODCMD_FUNC(chan_opt_topicmask
)
5947 struct chanData
*cData
= channel
->channel_info
;
5950 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5952 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5956 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5958 if(cData
->topic_mask
)
5959 free(cData
->topic_mask
);
5960 if(mask
[0] == '*' && mask
[1] == 0)
5962 cData
->topic_mask
= 0;
5966 cData
->topic_mask
= strdup(mask
);
5968 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5969 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5970 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5974 if(channel
->channel_info
->topic_mask
)
5975 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5977 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5981 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5985 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5989 if(greeting
[0] == '*' && greeting
[1] == 0)
5993 unsigned int length
= strlen(greeting
);
5994 if(length
> chanserv_conf
.greeting_length
)
5996 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5999 *data
= strdup(greeting
);
6008 reply(name
, user_find_message(user
, "MSG_NONE"));
6012 static MODCMD_FUNC(chan_opt_greeting
)
6014 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6017 static MODCMD_FUNC(chan_opt_usergreeting
)
6019 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6022 static MODCMD_FUNC(chan_opt_modes
)
6024 struct mod_chanmode
*new_modes
;
6025 char modes
[MODELEN
];
6029 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6030 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6034 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6036 reply("CSMSG_NO_ACCESS");
6039 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6041 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6043 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6045 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6048 else if(new_modes
->argc
> 1)
6050 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6051 mod_chanmode_free(new_modes
);
6056 channel
->channel_info
->modes
= *new_modes
;
6057 modcmd_chanmode_announce(new_modes
);
6058 mod_chanmode_free(new_modes
);
6062 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6064 reply("CSMSG_SET_MODES", modes
);
6066 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6070 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6072 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6074 struct chanData
*cData
= channel
->channel_info
;
6079 /* Set flag according to value. */
6080 if(enabled_string(argv
[1]))
6082 cData
->flags
|= mask
;
6085 else if(disabled_string(argv
[1]))
6087 cData
->flags
&= ~mask
;
6092 reply("MSG_INVALID_BINARY", argv
[1]);
6098 /* Find current option value. */
6099 value
= (cData
->flags
& mask
) ? 1 : 0;
6103 reply(name
, user_find_message(user
, "MSG_ON"));
6105 reply(name
, user_find_message(user
, "MSG_OFF"));
6109 static MODCMD_FUNC(chan_opt_nodelete
)
6111 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6113 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6117 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6120 static MODCMD_FUNC(chan_opt_dynlimit
)
6122 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6125 static MODCMD_FUNC(chan_opt_offchannel
)
6127 struct chanData
*cData
= channel
->channel_info
;
6132 /* Set flag according to value. */
6133 if(enabled_string(argv
[1]))
6135 if(!IsOffChannel(cData
))
6136 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6137 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6140 else if(disabled_string(argv
[1]))
6142 if(IsOffChannel(cData
))
6144 struct mod_chanmode change
;
6145 mod_chanmode_init(&change
);
6147 change
.args
[0].mode
= MODE_CHANOP
;
6148 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6149 mod_chanmode_announce(chanserv
, channel
, &change
);
6151 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6156 reply("MSG_INVALID_BINARY", argv
[1]);
6162 /* Find current option value. */
6163 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6167 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6169 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6173 static MODCMD_FUNC(chan_opt_defaults
)
6175 struct userData
*uData
;
6176 struct chanData
*cData
;
6177 const char *confirm
;
6178 enum levelOption lvlOpt
;
6179 enum charOption chOpt
;
6181 cData
= channel
->channel_info
;
6182 uData
= GetChannelUser(cData
, user
->handle_info
);
6183 if(!uData
|| (uData
->access
< UL_OWNER
))
6185 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6188 confirm
= make_confirmation_string(uData
);
6189 if((argc
< 2) || strcmp(argv
[1], confirm
))
6191 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6194 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6195 cData
->modes
= chanserv_conf
.default_modes
;
6196 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6197 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6198 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6199 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6200 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6205 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6207 struct chanData
*cData
= channel
->channel_info
;
6208 struct userData
*uData
;
6209 unsigned short value
;
6213 if(!check_user_level(channel
, user
, option
, 1, 1))
6215 reply("CSMSG_CANNOT_SET");
6218 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6219 if(!value
&& strcmp(argv
[1], "0"))
6221 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6224 uData
= GetChannelUser(cData
, user
->handle_info
);
6225 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6227 reply("CSMSG_BAD_SETLEVEL");
6233 /* This test only applies to owners, since non-owners
6234 * trying to set an option to above their level get caught
6235 * by the CSMSG_BAD_SETLEVEL test above.
6237 if(value
> uData
->access
)
6239 reply("CSMSG_BAD_SETTERS");
6246 cData
->lvlOpts
[option
] = value
;
6248 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6252 static MODCMD_FUNC(chan_opt_enfops
)
6254 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6257 static MODCMD_FUNC(chan_opt_enfhalfops
)
6259 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6261 static MODCMD_FUNC(chan_opt_enfmodes
)
6263 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6266 static MODCMD_FUNC(chan_opt_enftopic
)
6268 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6271 static MODCMD_FUNC(chan_opt_pubcmd
)
6273 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6276 static MODCMD_FUNC(chan_opt_setters
)
6278 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6281 static MODCMD_FUNC(chan_opt_userinfo
)
6283 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6286 static MODCMD_FUNC(chan_opt_topicsnarf
)
6288 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6291 static MODCMD_FUNC(chan_opt_inviteme
)
6293 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6296 /* TODO: Make look like this when no args are
6298 * -X3- -------------------------------
6299 * -X3- BanTimeout: Bans are removed:
6300 * -X3- ----- * indicates current -----
6301 * -X3- 0: [*] Never.
6302 * -X3- 1: [ ] After 10 minutes.
6303 * -X3- 2: [ ] After 2 hours.
6304 * -X3- 3: [ ] After 4 hours.
6305 * -X3- 4: [ ] After 24 hours.
6306 * -X3- 5: [ ] After one week.
6307 * -X3- ------------- End -------------
6310 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6312 struct chanData
*cData
= channel
->channel_info
;
6313 int count
= charOptions
[option
].count
, index
;
6317 index
= atoi(argv
[1]);
6319 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6321 reply("CSMSG_INVALID_NUMERIC", index
);
6322 /* Show possible values. */
6323 for(index
= 0; index
< count
; index
++)
6324 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6328 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6332 /* Find current option value. */
6335 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6339 /* Somehow, the option value is corrupt; reset it to the default. */
6340 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6345 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6349 static MODCMD_FUNC(chan_opt_automode
)
6351 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6354 static MODCMD_FUNC(chan_opt_protect
)
6356 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6359 static MODCMD_FUNC(chan_opt_toys
)
6361 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6364 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6366 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6369 static MODCMD_FUNC(chan_opt_bantimeout
)
6371 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6374 static MODCMD_FUNC(chan_opt_topicrefresh
)
6376 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6379 static MODCMD_FUNC(chan_opt_resync
)
6381 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6384 static struct svccmd_list set_shows_list
;
6387 handle_svccmd_unbind(struct svccmd
*target
) {
6389 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6390 if(target
== set_shows_list
.list
[ii
])
6391 set_shows_list
.used
= 0;
6394 static CHANSERV_FUNC(cmd_set
)
6396 struct svccmd
*subcmd
;
6400 /* Check if we need to (re-)initialize set_shows_list. */
6401 if(!set_shows_list
.used
)
6403 if(!set_shows_list
.size
)
6405 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6406 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6408 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6410 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6411 sprintf(buf
, "%s %s", argv
[0], name
);
6412 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6415 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6418 svccmd_list_append(&set_shows_list
, subcmd
);
6424 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6425 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6427 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6429 subcmd
= set_shows_list
.list
[ii
];
6430 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6432 reply("CSMSG_CHANNEL_OPTIONS_END");
6436 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6437 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6440 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6443 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6445 reply("CSMSG_NO_ACCESS");
6449 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6453 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6455 struct userData
*uData
;
6457 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6460 reply("CSMSG_NOT_USER", channel
->name
);
6466 /* Just show current option value. */
6468 else if(enabled_string(argv
[1]))
6470 uData
->flags
|= mask
;
6472 else if(disabled_string(argv
[1]))
6474 uData
->flags
&= ~mask
;
6478 reply("MSG_INVALID_BINARY", argv
[1]);
6482 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6486 static MODCMD_FUNC(user_opt_autoop
)
6488 struct userData
*uData
;
6490 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6493 reply("CSMSG_NOT_USER", channel
->name
);
6496 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6497 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6499 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6502 static MODCMD_FUNC(user_opt_autoinvite
)
6504 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6507 static MODCMD_FUNC(user_opt_autojoin
)
6509 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6512 static MODCMD_FUNC(user_opt_info
)
6514 struct userData
*uData
;
6517 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6521 /* If they got past the command restrictions (which require access)
6522 * but fail this test, we have some fool with security override on.
6524 reply("CSMSG_NOT_USER", channel
->name
);
6531 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6532 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6534 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6537 bp
= strcspn(infoline
, "\001");
6540 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6545 if(infoline
[0] == '*' && infoline
[1] == 0)
6548 uData
->info
= strdup(infoline
);
6551 reply("CSMSG_USET_INFO", uData
->info
);
6553 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6557 struct svccmd_list uset_shows_list
;
6559 static CHANSERV_FUNC(cmd_uset
)
6561 struct svccmd
*subcmd
;
6565 /* Check if we need to (re-)initialize uset_shows_list. */
6566 if(!uset_shows_list
.used
)
6570 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6573 if(!uset_shows_list
.size
)
6575 uset_shows_list
.size
= ArrayLength(options
);
6576 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6578 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6580 const char *name
= options
[ii
];
6581 sprintf(buf
, "%s %s", argv
[0], name
);
6582 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6585 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6588 svccmd_list_append(&uset_shows_list
, subcmd
);
6594 /* Do this so options are presented in a consistent order. */
6595 reply("CSMSG_USER_OPTIONS");
6596 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6597 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6601 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6602 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6605 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6609 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6612 static CHANSERV_FUNC(cmd_giveownership
)
6614 struct handle_info
*new_owner_hi
;
6615 struct userData
*new_owner
, *curr_user
;
6616 struct chanData
*cData
= channel
->channel_info
;
6617 struct do_not_register
*dnr
;
6618 struct giveownership
*giveownership
;
6619 unsigned int force
, override
;
6620 unsigned short co_access
, new_owner_old_access
;
6621 char transfer_reason
[MAXLEN
];
6624 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6625 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6627 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6628 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6629 && (uData
->access
> 500)
6630 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6631 || uData
->access
< 500));
6634 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6636 struct userData
*owner
= NULL
;
6637 for(curr_user
= channel
->channel_info
->users
;
6639 curr_user
= curr_user
->next
)
6641 if(curr_user
->access
!= UL_OWNER
)
6645 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6652 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6654 char delay
[INTERVALLEN
];
6655 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6656 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6660 reply("CSMSG_NO_OWNER", channel
->name
);
6663 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6665 if(new_owner_hi
== user
->handle_info
)
6667 reply("CSMSG_NO_TRANSFER_SELF");
6670 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6675 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
, 0);
6679 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6683 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6685 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6688 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6689 if(!IsHelping(user
))
6690 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6692 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6696 new_owner_old_access
= new_owner
->access
;
6697 if(new_owner
->access
>= UL_COOWNER
)
6698 co_access
= new_owner
->access
;
6700 co_access
= UL_COOWNER
;
6701 new_owner
->access
= UL_OWNER
;
6703 curr_user
->access
= co_access
;
6704 cData
->ownerTransfer
= now
;
6706 giveownership
= calloc(1, sizeof(*giveownership
));
6707 giveownership
->issued
= now
;
6708 giveownership
->old_owner
= curr_user
->handle
->handle
;
6709 giveownership
->target
= new_owner_hi
->handle
;
6710 giveownership
->target_access
= new_owner_old_access
;
6713 if(argc
> (2 + force
))
6715 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6716 giveownership
->reason
= strdup(transfer_reason
);
6718 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6721 giveownership
->previous
= channel
->channel_info
->giveownership
;
6722 channel
->channel_info
->giveownership
= giveownership
;
6724 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6725 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6726 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6731 chanserv_expire_user_suspension(void *data
)
6733 struct userData
*target
= data
;
6735 target
->expires
= 0;
6736 target
->flags
&= ~USER_SUSPENDED
;
6739 static CHANSERV_FUNC(cmd_suspend
)
6741 struct handle_info
*hi
;
6742 struct userData
*self
, *target
;
6746 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6747 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6748 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6750 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6753 if(target
->access
>= self
->access
)
6755 reply("MSG_USER_OUTRANKED", hi
->handle
);
6758 if(target
->flags
& USER_SUSPENDED
)
6760 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6765 target
->present
= 0;
6768 if(!strcmp(argv
[2], "0"))
6772 unsigned int duration
;
6773 if(!(duration
= ParseInterval(argv
[2])))
6775 reply("MSG_INVALID_DURATION", argv
[2]);
6778 expiry
= now
+ duration
;
6781 target
->expires
= expiry
;
6784 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6786 target
->flags
|= USER_SUSPENDED
;
6787 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6791 static CHANSERV_FUNC(cmd_unsuspend
)
6793 struct handle_info
*hi
;
6794 struct userData
*self
, *target
;
6797 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6798 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6799 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6801 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6804 if(target
->access
>= self
->access
)
6806 reply("MSG_USER_OUTRANKED", hi
->handle
);
6809 if(!(target
->flags
& USER_SUSPENDED
))
6811 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6814 target
->flags
&= ~USER_SUSPENDED
;
6815 scan_user_presence(target
, NULL
);
6816 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6817 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6821 static MODCMD_FUNC(cmd_deleteme
)
6823 struct handle_info
*hi
;
6824 struct userData
*target
;
6825 const char *confirm_string
;
6826 unsigned short access
;
6829 hi
= user
->handle_info
;
6830 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6832 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6835 if(target
->access
== UL_OWNER
)
6837 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6840 confirm_string
= make_confirmation_string(target
);
6841 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6843 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6846 access
= target
->access
;
6847 channel_name
= strdup(channel
->name
);
6848 del_channel_user(target
, 1);
6849 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6855 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6857 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6858 struct chanData
*cData
;
6861 for(cData
= channelList
; cData
; cData
= cData
->next
)
6863 if(IsSuspended(cData
))
6865 opt
= cData
->chOpts
[chTopicRefresh
];
6868 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6871 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6872 cData
->last_refresh
= refresh_num
;
6874 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6878 chanserv_auto_resync(UNUSED_ARG(void *data
))
6880 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6881 struct chanData
*cData
;
6884 for(cData
= channelList
; cData
; cData
= cData
->next
)
6886 if(IsSuspended(cData
)) continue;
6887 opt
= cData
->chOpts
[chResync
];
6888 if(opt
== 'n') continue;
6889 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6890 resync_channel(cData
->channel
);
6891 cData
->last_resync
= refresh_num
;
6893 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6896 static CHANSERV_FUNC(cmd_unf
)
6900 char response
[MAXLEN
];
6901 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6902 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6903 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6906 reply("CSMSG_UNF_RESPONSE");
6910 static CHANSERV_FUNC(cmd_ping
)
6914 char response
[MAXLEN
];
6915 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6916 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6917 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6920 reply("CSMSG_PING_RESPONSE");
6924 static CHANSERV_FUNC(cmd_wut
)
6928 char response
[MAXLEN
];
6929 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6930 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6931 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6934 reply("CSMSG_WUT_RESPONSE");
6939 static CHANSERV_FUNC(cmd_8ball
)
6941 unsigned int i
, j
, accum
;
6946 for(i
=1; i
<argc
; i
++)
6947 for(j
=0; argv
[i
][j
]; j
++)
6948 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6949 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6952 char response
[MAXLEN
];
6953 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6954 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6957 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6961 #else /* Use cool 8ball instead */
6963 void eightball(char *outcome
, int method
, unsigned int seed
)
6967 #define NUMOFCOLORS 18
6968 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6969 "white", "black", "grey", "brown",
6970 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6971 "fuchsia","turquoise","magenta", "cyan"};
6972 #define NUMOFLOCATIONS 50
6973 char balllocations
[50][55] = {
6974 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6975 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6976 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6977 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6978 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6979 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6980 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6981 "your bra", "your hair", "your bed", "the couch", "the wall",
6982 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6983 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6984 #define NUMOFPREPS 15
6985 char ballpreps
[50][50] = {
6986 "Near", "Somewhere near", "In", "In", "In",
6987 "In", "Hiding in", "Under", "Next to", "Over",
6988 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6989 #define NUMOFNUMS 34
6990 char ballnums
[50][50] = {
6991 "A hundred", "A thousand", "A few", "42",
6992 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6993 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6994 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6996 #define NUMOFMULTS 8
6997 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7000 * 0: normal (Not used in x3)
7007 if (method
== 1) /* A Color */
7011 answer
= (rand() % 12); /* Make sure this is the # of entries */
7014 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
7016 case 1: strcpy(tmp
, "Sort of a light %s color.");
7018 case 2: strcpy(tmp
, "Dark and dreary %s.");
7020 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
7022 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
7024 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
7026 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
7028 case 10: strcpy(tmp
, "Solid %s.");
7030 case 11: strcpy(tmp
, "Transparent %s.");
7032 default: strcpy(outcome
, "An invalid random number was generated.");
7035 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
7038 else if (method
== 2) /* Location */
7040 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
7042 else if (method
== 3) /* Number of ___ */
7044 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
7048 //Debug(DBGWARNING, "Error in 8ball.");
7053 static CHANSERV_FUNC(cmd_8ball
)
7055 char *word1
, *word2
, *word3
;
7056 static char eb
[MAXLEN
];
7057 unsigned int accum
, i
, j
;
7061 for(i
=1; i
<argc
; i
++)
7062 for(j
=0; argv
[i
][j
]; j
++)
7063 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7065 accum
+= time(NULL
)/3600;
7067 word2
= argc
>2?argv
[2]:"";
7068 word3
= argc
>3?argv
[3]:"";
7071 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
7072 eightball(eb
, 1, accum
);
7073 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7074 eightball(eb
, 1, accum
);
7075 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7076 eightball(eb
, 1, accum
);
7077 /*** LOCATION *****/
7082 (strcasecmp(word1
, "where") == 0) &&
7083 (strcasecmp(word2
, "is") == 0)
7087 strcasecmp(word1
, "where's") == 0
7090 eightball(eb
, 2, accum
);
7092 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
7093 eightball(eb
, 3, accum
);
7097 /* Generic 8ball question.. so pull from x3.conf srvx style */
7100 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7103 char response
[MAXLEN
];
7104 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
7105 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7108 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7114 char response
[MAXLEN
];
7115 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
7116 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7119 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
7124 static CHANSERV_FUNC(cmd_d
)
7126 unsigned long sides
, count
, modifier
, ii
, total
;
7127 char response
[MAXLEN
], *sep
;
7131 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
7141 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
7142 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
7146 else if((sep
[0] == '-') && isdigit(sep
[1]))
7147 modifier
= strtoul(sep
, NULL
, 10);
7148 else if((sep
[0] == '+') && isdigit(sep
[1]))
7149 modifier
= strtoul(sep
+1, NULL
, 10);
7156 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
7161 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7164 for(total
= ii
= 0; ii
< count
; ++ii
)
7165 total
+= (rand() % sides
) + 1;
7168 if((count
> 1) || modifier
)
7170 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7171 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7175 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7176 sprintf(response
, fmt
, total
, sides
);
7179 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7181 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7185 static CHANSERV_FUNC(cmd_huggle
)
7187 /* CTCP must be via PRIVMSG, never notice */
7189 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7191 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7195 static CHANSERV_FUNC(cmd_calc
)
7197 char response
[MAXLEN
];
7200 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7203 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7205 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7209 static CHANSERV_FUNC(cmd_reply
)
7213 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7216 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7218 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7223 chanserv_adjust_limit(void *data
)
7225 struct mod_chanmode change
;
7226 struct chanData
*cData
= data
;
7227 struct chanNode
*channel
= cData
->channel
;
7230 if(IsSuspended(cData
))
7233 cData
->limitAdjusted
= now
;
7234 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7235 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7237 if(limit
> cData
->modes
.new_limit
)
7238 limit
= cData
->modes
.new_limit
;
7239 else if(limit
== cData
->modes
.new_limit
)
7243 mod_chanmode_init(&change
);
7244 change
.modes_set
= MODE_LIMIT
;
7245 change
.new_limit
= limit
;
7246 mod_chanmode_announce(chanserv
, channel
, &change
);
7250 handle_new_channel(struct chanNode
*channel
)
7252 struct chanData
*cData
;
7254 if(!(cData
= channel
->channel_info
))
7257 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7258 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7260 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7261 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7265 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
7267 struct banData
*bData
;
7268 struct mod_chanmode
*change
;
7270 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
7275 if (chan
->channel_info
) {
7276 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
7278 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
))
7290 check_bans(struct userNode
*user
, const char *channel
)
7292 struct chanNode
*chan
;
7293 struct mod_chanmode change
;
7294 struct chanData
*cData
;
7295 struct banData
*bData
;
7297 if (!(chan
= GetChannel(channel
)))
7300 if(!(cData
= chan
->channel_info
))
7303 mod_chanmode_init(&change
);
7306 if(chan
->banlist
.used
< MAXBANS
)
7308 /* Not joining through a ban. */
7309 for(bData
= cData
->bans
;
7310 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7311 bData
= bData
->next
);
7315 char kick_reason
[MAXLEN
];
7316 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7318 bData
->triggered
= now
;
7319 if(bData
!= cData
->bans
)
7321 /* Shuffle the ban to the head of the list. */
7323 bData
->next
->prev
= bData
->prev
;
7325 bData
->prev
->next
= bData
->next
;
7328 bData
->next
= cData
->bans
;
7331 cData
->bans
->prev
= bData
;
7333 cData
->bans
= bData
;
7336 change
.args
[0].mode
= MODE_BAN
;
7337 change
.args
[0].u
.hostmask
= bData
->mask
;
7338 mod_chanmode_announce(chanserv
, chan
, &change
);
7339 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
7347 /* Welcome to my worst nightmare. Warning: Read (or modify)
7348 the code below at your own risk. */
7350 handle_join(struct modeNode
*mNode
)
7352 struct mod_chanmode change
;
7353 struct userNode
*user
= mNode
->user
;
7354 struct chanNode
*channel
= mNode
->channel
;
7355 struct chanData
*cData
;
7356 struct userData
*uData
= NULL
;
7357 struct banData
*bData
;
7358 struct handle_info
*handle
;
7359 unsigned int modes
= 0, info
= 0;
7362 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7365 cData
= channel
->channel_info
;
7366 if(channel
->members
.used
> cData
->max
)
7367 cData
->max
= channel
->members
.used
;
7370 /* Check for bans. If they're joining through a ban, one of two
7372 * 1: Join during a netburst, by riding the break. Kick them
7373 * unless they have ops or voice in the channel.
7374 * 2: They're allowed to join through the ban (an invite in
7375 * ircu2.10, or a +e on Hybrid, or something).
7376 * If they're not joining through a ban, and the banlist is not
7377 * full, see if they're on the banlist for the channel. If so,
7380 if(user
->uplink
->burst
&& !mNode
->modes
)
7383 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7385 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7387 /* Riding a netburst. Naughty. */
7388 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7395 if(user
->handle_info
)
7397 handle
= user
->handle_info
;
7400 uData
= GetTrueChannelAccess(cData
, handle
);
7405 mod_chanmode_init(&change
);
7408 /* TODO: maybe only people above inviteme level? -Rubin */
7409 /* We don't kick people with access */
7412 if(channel
->banlist
.used
< MAXBANS
)
7414 /* Not joining through a ban. */
7415 for(bData
= cData
->bans
;
7416 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7417 bData
= bData
->next
);
7421 char kick_reason
[MAXLEN
];
7422 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7424 bData
->triggered
= now
;
7425 if(bData
!= cData
->bans
)
7427 /* Shuffle the ban to the head of the list. */
7429 bData
->next
->prev
= bData
->prev
;
7431 bData
->prev
->next
= bData
->next
;
7434 bData
->next
= cData
->bans
;
7437 cData
->bans
->prev
= bData
;
7438 cData
->bans
= bData
;
7441 change
.args
[0].mode
= MODE_BAN
;
7442 change
.args
[0].u
.hostmask
= bData
->mask
;
7443 mod_chanmode_announce(chanserv
, channel
, &change
);
7444 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7450 /* ChanServ will not modify the limits in join-flooded channels.
7451 It will also skip DynLimit processing when the user (or srvx)
7452 is bursting in, because there are likely more incoming. */
7453 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7454 && !user
->uplink
->burst
7455 && !channel
->join_flooded
7456 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7458 /* The user count has begun "bumping" into the channel limit,
7459 so set a timer to raise the limit a bit. Any previous
7460 timers are removed so three incoming users within the delay
7461 results in one limit change, not three. */
7463 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7464 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7467 /* Give automodes exept during join-floods */
7468 if(!channel
->join_flooded
)
7470 if(cData
->chOpts
[chAutomode
] == 'v')
7471 modes
|= MODE_VOICE
;
7472 else if(cData
->chOpts
[chAutomode
] == 'h')
7473 modes
|= MODE_HALFOP
;
7474 else if(cData
->chOpts
[chAutomode
] == 'o')
7475 modes
|= MODE_CHANOP
;
7478 greeting
= cData
->greeting
;
7479 if(user
->handle_info
)
7481 /* handle = user->handle_info; */
7483 if(IsHelper(user
) && !IsHelping(user
))
7486 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7488 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7490 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7496 /* uData = GetTrueChannelAccess(cData, handle); */
7497 if(uData
&& !IsUserSuspended(uData
))
7499 /* non users getting automodes are handled above. */
7500 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7502 /* just op everyone with access */
7503 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7504 modes
|= MODE_VOICE
;
7505 /* or do their access level */
7506 else if(uData
->access
>= UL_OP
)
7507 modes
|= MODE_CHANOP
;
7508 else if(uData
->access
>= UL_HALFOP
)
7509 modes
|= MODE_HALFOP
;
7510 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7511 modes
|= MODE_VOICE
;
7513 if(uData
->access
>= UL_PRESENT
)
7514 cData
->visited
= now
;
7515 if(cData
->user_greeting
)
7516 greeting
= cData
->user_greeting
;
7518 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7519 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7527 /* If user joining normally (not during burst), apply op or voice,
7528 * and send greeting/userinfo as appropriate.
7530 if(!user
->uplink
->burst
)
7534 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7535 if(modes & MODE_CHANOP) {
7536 modes &= ~MODE_HALFOP;
7537 modes &= ~MODE_VOICE;
7540 change
.args
[0].mode
= modes
;
7541 change
.args
[0].u
.member
= mNode
;
7542 mod_chanmode_announce(chanserv
, channel
, &change
);
7545 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7547 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7553 chanserv_autojoin_channels(struct userNode
*user
)
7555 struct userData
*channel
;
7557 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7559 struct chanNode
*cn
;
7560 struct modeNode
*mn
;
7562 if(IsUserSuspended(channel
)
7563 || IsSuspended(channel
->channel
)
7564 || !(cn
= channel
->channel
->channel
))
7567 mn
= GetUserMode(cn
, user
);
7570 if(!IsUserSuspended(channel
)
7571 && IsUserAutoJoin(channel
)
7572 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7574 && !user
->uplink
->burst
)
7575 irc_svsjoin(chanserv
, user
, cn
);
7581 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7583 struct mod_chanmode change
;
7584 struct userData
*channel
;
7585 unsigned int ii
, jj
, i
;
7587 if(!user
->handle_info
)
7590 mod_chanmode_init(&change
);
7592 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7594 struct chanNode
*cn
;
7595 struct chanData
*cData
;
7596 struct modeNode
*mn
;
7597 if(IsUserSuspended(channel
)
7598 || IsSuspended(channel
->channel
)
7599 || !(cn
= channel
->channel
->channel
))
7602 cData
= cn
->channel_info
;
7603 mn
= GetUserMode(cn
, user
);
7606 if(!IsUserSuspended(channel
)
7607 && IsUserAutoInvite(channel
)
7608 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7610 && !user
->uplink
->burst
)
7611 irc_invite(chanserv
, user
, cn
);
7615 if(channel
->access
>= UL_PRESENT
)
7616 channel
->channel
->visited
= now
;
7618 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
7620 if(channel
->access
>= UL_OP
)
7621 change
.args
[0].mode
= MODE_CHANOP
;
7622 else if(channel
->access
>= UL_HALFOP
)
7623 change
.args
[0].mode
= MODE_HALFOP
;
7624 else if(channel
->access
>= UL_PEON
)
7625 change
.args
[0].mode
= MODE_VOICE
;
7627 change
.args
[0].mode
= 0;
7628 change
.args
[0].u
.member
= mn
;
7629 if(change
.args
[0].mode
)
7630 mod_chanmode_announce(chanserv
, cn
, &change
);
7633 channel
->seen
= now
;
7634 channel
->present
= 1;
7637 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7639 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7640 struct banData
*ban
;
7642 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7643 || !channel
->channel_info
7644 || IsSuspended(channel
->channel_info
))
7646 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7648 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7649 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7651 if(jj
< channel
->banlist
.used
)
7653 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7655 char kick_reason
[MAXLEN
];
7656 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7658 change
.args
[0].mode
= MODE_BAN
;
7659 change
.args
[0].u
.hostmask
= ban
->mask
;
7660 mod_chanmode_announce(chanserv
, channel
, &change
);
7661 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7662 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7663 ban
->triggered
= now
;
7668 if(IsSupportHelper(user
))
7670 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7672 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7674 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7680 if (user
->handle_info
->ignores
->used
) {
7681 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7682 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7686 if (user
->handle_info
->epithet
)
7687 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7689 /* process autojoin channels 5 seconds later as this sometimes
7690 happens before autohide */
7691 // timeq_add(now + 5, chanserv_autojoin_channels, user);
7692 chanserv_autojoin_channels(user
);
7696 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7698 struct chanData
*cData
;
7699 struct userData
*uData
;
7701 cData
= mn
->channel
->channel_info
;
7702 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7705 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7707 /* Allow for a bit of padding so that the limit doesn't
7708 track the user count exactly, which could get annoying. */
7709 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7711 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7712 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7716 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7718 scan_user_presence(uData
, mn
->user
);
7722 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7724 unsigned int ii
, jj
;
7725 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7727 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7728 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7730 if(jj
< mn
->user
->channels
.used
)
7733 if(ii
== chanserv_conf
.support_channels
.used
)
7734 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7739 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7741 struct userData
*uData
;
7743 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7744 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7745 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7748 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
7750 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7751 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7754 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7759 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7761 struct chanData
*cData
;
7763 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7766 cData
= channel
->channel_info
;
7767 if(bad_topic(channel
, user
, channel
->topic
))
7768 { /* User doesnt have privs to set topics. Undo it */
7769 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7770 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7773 /* If there is a topic mask set, and the new topic doesnt match,
7774 * set the topic to mask + new_topic */
7775 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7777 char new_topic
[TOPICLEN
+1];
7778 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7781 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7782 /* and fall through to topicsnarf code below.. */
7784 else /* Topic couldnt fit into mask, was too long */
7786 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7787 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7788 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7792 /* With topicsnarf, grab the topic and save it as the default topic. */
7793 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7796 cData
->topic
= strdup(channel
->topic
);
7802 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7804 struct mod_chanmode
*bounce
= NULL
;
7805 unsigned int bnc
, ii
;
7808 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7811 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7812 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7814 char correct
[MAXLEN
];
7815 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7816 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7817 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7819 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7821 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7823 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7824 if(!protect_user(victim
, user
, channel
->channel_info
, false))
7827 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7830 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7831 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7832 if(bounce
->args
[bnc
].u
.member
)
7836 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7837 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7839 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7841 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7843 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7844 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7847 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7848 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7849 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7852 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7854 const char *ban
= change
->args
[ii
].u
.hostmask
;
7855 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7858 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7859 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7860 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7862 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7867 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7868 mod_chanmode_announce(chanserv
, channel
, bounce
);
7869 for(ii
= 0; ii
< change
->argc
; ++ii
)
7870 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7871 free((char*)bounce
->args
[ii
].u
.hostmask
);
7872 mod_chanmode_free(bounce
);
7877 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7879 struct chanNode
*channel
;
7880 struct banData
*bData
;
7881 struct mod_chanmode change
;
7882 unsigned int ii
, jj
;
7883 char kick_reason
[MAXLEN
];
7885 mod_chanmode_init(&change
);
7887 change
.args
[0].mode
= MODE_BAN
;
7888 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7890 channel
= user
->channels
.list
[ii
]->channel
;
7891 /* Need not check for bans if they're opped or voiced. */
7892 /* TODO: does this make sense in automode v, h, and o? *
7893 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7894 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7896 /* Need not check for bans unless channel registration is active. */
7897 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7899 /* Look for a matching ban already on the channel. */
7900 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7901 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7903 /* Need not act if we found one. */
7904 if(jj
< channel
->banlist
.used
)
7906 /* don't kick someone on the userlist */
7907 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7909 /* Look for a matching ban in this channel. */
7910 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7912 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7914 change
.args
[0].u
.hostmask
= bData
->mask
;
7915 mod_chanmode_announce(chanserv
, channel
, &change
);
7916 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7917 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7918 bData
->triggered
= now
;
7919 break; /* we don't need to check any more bans in the channel */
7924 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7926 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7930 dict_remove2(handle_dnrs
, old_handle
, 1);
7931 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7932 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7937 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7939 struct userNode
*h_user
;
7941 if(handle
->channels
)
7943 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7944 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7946 while(handle
->channels
)
7947 del_channel_user(handle
->channels
, 1);
7952 handle_server_link(UNUSED_ARG(struct server
*server
))
7954 struct chanData
*cData
;
7956 for(cData
= channelList
; cData
; cData
= cData
->next
)
7958 if(!IsSuspended(cData
))
7959 cData
->may_opchan
= 1;
7960 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7961 && !cData
->channel
->join_flooded
7962 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7963 < chanserv_conf
.adjust_threshold
))
7965 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7966 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7972 chanserv_conf_read(void)
7976 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7977 struct mod_chanmode
*change
;
7978 struct string_list
*strlist
;
7979 struct chanNode
*chan
;
7982 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7984 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7987 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7988 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7989 chanserv_conf
.support_channels
.used
= 0;
7990 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7992 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7994 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7997 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7999 channelList_append(&chanserv_conf
.support_channels
, chan
);
8002 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
8005 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8008 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
8010 channelList_append(&chanserv_conf
.support_channels
, chan
);
8012 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
8013 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
8014 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
8015 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
8016 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
8017 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
8018 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
8019 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
8020 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
8021 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
8022 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
8023 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
8024 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
8025 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
8026 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
8027 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
8028 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
8029 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
8030 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
8031 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
8032 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
8033 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
8034 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
8035 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
8036 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
8038 NickChange(chanserv
, str
, 0);
8039 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
8040 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
8041 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
8042 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
8043 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
8044 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
8045 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
8046 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
8047 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
8048 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
8049 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
8050 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
8051 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
8052 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
8053 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
8054 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
8055 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
8056 god_timeout
= str
? ParseInterval(str
) : 60*15;
8057 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
8060 safestrncpy(mode_line
, str
, sizeof(mode_line
));
8061 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
8062 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
8063 && (change
->argc
< 2))
8065 chanserv_conf
.default_modes
= *change
;
8066 mod_chanmode_free(change
);
8068 free_string_list(chanserv_conf
.set_shows
);
8069 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
8071 strlist
= string_list_copy(strlist
);
8074 static const char *list
[] = {
8075 /* free form text */
8076 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8077 /* options based on user level */
8078 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8079 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
8080 /* multiple choice options */
8081 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
8082 /* binary options */
8083 "DynLimit", "NoDelete", "BanTimeout",
8088 strlist
= alloc_string_list(ArrayLength(list
)-1);
8089 for(ii
=0; list
[ii
]; ii
++)
8090 string_list_append(strlist
, strdup(list
[ii
]));
8092 chanserv_conf
.set_shows
= strlist
;
8093 /* We don't look things up now, in case the list refers to options
8094 * defined by modules initialized after this point. Just mark the
8095 * function list as invalid, so it will be initialized.
8097 set_shows_list
.used
= 0;
8098 free_string_list(chanserv_conf
.eightball
);
8099 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
8102 strlist
= string_list_copy(strlist
);
8106 strlist
= alloc_string_list(4);
8107 string_list_append(strlist
, strdup("Yes."));
8108 string_list_append(strlist
, strdup("No."));
8109 string_list_append(strlist
, strdup("Maybe so."));
8111 chanserv_conf
.eightball
= strlist
;
8112 free_string_list(chanserv_conf
.old_ban_names
);
8113 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
8115 strlist
= string_list_copy(strlist
);
8117 strlist
= alloc_string_list(2);
8118 chanserv_conf
.old_ban_names
= strlist
;
8119 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
8120 off_channel
= str
? atoi(str
) : 0;
8124 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
8127 struct note_type
*ntype
;
8130 if(!(obj
= GET_RECORD_OBJECT(rd
)))
8132 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
8135 if(!(ntype
= chanserv_create_note_type(key
)))
8137 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
8141 /* Figure out set access */
8142 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
8144 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8145 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
8147 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
8149 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
8150 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
8152 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
8154 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
8158 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
8159 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8160 ntype
->set_access
.min_opserv
= 0;
8163 /* Figure out visibility */
8164 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
8165 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8166 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
8167 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8168 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
8169 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
8170 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
8171 ntype
->visible_type
= NOTE_VIS_ALL
;
8173 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8175 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
8176 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
8180 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8182 struct handle_info
*handle
;
8183 struct userData
*uData
;
8184 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
8186 unsigned short access
, lastaccess
= 0;
8188 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8190 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
8194 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
8195 if(access
> UL_OWNER
)
8197 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
8201 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
8202 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
8203 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
8204 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
8205 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8206 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
8207 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
8208 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
8209 lastaccess
= lstacc
? atoi(lstacc
) : 0;
8211 handle
= get_handle_info(key
);
8214 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
8218 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
, 0);
8219 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
8220 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
8222 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
8223 if (uData
->accessexpiry
> 0)
8224 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
8226 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
8227 if (uData
->clvlexpiry
> 0)
8228 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
8230 uData
->lastaccess
= lastaccess
;
8232 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
8234 if(uData
->expires
> now
)
8235 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
8237 uData
->flags
&= ~USER_SUSPENDED
;
8240 /* Upgrade: set autoop to the inverse of noautoop */
8241 if(chanserv_read_version
< 2)
8243 /* if noautoop is true, set autoop false, and vice versa */
8244 if(uData
->flags
& USER_NOAUTO_OP
)
8245 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
8247 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
8248 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
);
8254 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8256 struct banData
*bData
;
8257 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
8258 time_t set_time
, triggered_time
, expires_time
;
8260 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8262 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8266 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8267 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8268 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8269 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8270 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8271 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8272 if (!reason
|| !owner
)
8275 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8276 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8278 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8280 expires_time
= set_time
+ atoi(s_duration
);
8284 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8287 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8290 static struct suspended
*
8291 chanserv_read_suspended(dict_t obj
)
8293 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8297 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8298 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8299 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8300 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8301 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8302 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8303 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8304 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8305 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8306 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8310 static struct giveownership
*
8311 chanserv_read_giveownership(dict_t obj
)
8313 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8317 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8318 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8320 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8322 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8323 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8325 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8326 giveownership
->reason
= str
? strdup(str
) : NULL
;
8327 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8328 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8330 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8331 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8332 return giveownership
;
8336 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8338 struct suspended
*suspended
;
8339 struct giveownership
*giveownership
;
8340 struct mod_chanmode
*modes
;
8341 struct chanNode
*cNode
;
8342 struct chanData
*cData
;
8343 struct dict
*channel
, *obj
;
8344 char *str
, *argv
[10];
8348 channel
= hir
->d
.object
;
8350 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8353 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8356 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8359 cData
= register_channel(cNode
, str
);
8362 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8366 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8368 enum levelOption lvlOpt
;
8369 enum charOption chOpt
;
8371 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8372 cData
->flags
= atoi(str
);
8374 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8376 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8378 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8379 else if(levelOptions
[lvlOpt
].old_flag
)
8381 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8382 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8384 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8388 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8390 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8392 cData
->chOpts
[chOpt
] = str
[0];
8395 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8397 enum levelOption lvlOpt
;
8398 enum charOption chOpt
;
8401 cData
->flags
= base64toint(str
, 5);
8402 count
= strlen(str
+= 5);
8403 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8406 if(levelOptions
[lvlOpt
].old_flag
)
8408 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8409 lvl
= levelOptions
[lvlOpt
].flag_value
;
8411 lvl
= levelOptions
[lvlOpt
].default_value
;
8413 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8415 case 'c': lvl
= UL_COOWNER
; break;
8416 case 'm': lvl
= UL_MANAGER
; break;
8417 case 'n': lvl
= UL_OWNER
+1; break;
8418 case 'o': lvl
= UL_OP
; break;
8419 case 'p': lvl
= UL_PEON
; break;
8420 case 'h': lvl
= UL_HALFOP
; break;
8421 case 'w': lvl
= UL_OWNER
; break;
8422 default: lvl
= 0; break;
8424 cData
->lvlOpts
[lvlOpt
] = lvl
;
8426 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8427 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8430 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8432 suspended
= chanserv_read_suspended(obj
);
8433 cData
->suspended
= suspended
;
8434 suspended
->cData
= cData
;
8435 /* We could use suspended->expires and suspended->revoked to
8436 * set the CHANNEL_SUSPENDED flag, but we don't. */
8438 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8440 suspended
= calloc(1, sizeof(*suspended
));
8441 suspended
->issued
= 0;
8442 suspended
->revoked
= 0;
8443 suspended
->suspender
= strdup(str
);
8444 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8445 suspended
->expires
= str
? atoi(str
) : 0;
8446 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8447 suspended
->reason
= strdup(str
? str
: "No reason");
8448 suspended
->previous
= NULL
;
8449 cData
->suspended
= suspended
;
8450 suspended
->cData
= cData
;
8454 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8455 suspended
= NULL
; /* to squelch a warning */
8458 if(IsSuspended(cData
)) {
8459 if(suspended
->expires
> now
)
8460 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8461 else if(suspended
->expires
)
8462 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8465 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8467 giveownership
= chanserv_read_giveownership(obj
);
8468 cData
->giveownership
= giveownership
;
8471 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8472 struct mod_chanmode change
;
8473 mod_chanmode_init(&change
);
8475 change
.args
[0].mode
= MODE_CHANOP
;
8476 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8477 mod_chanmode_announce(chanserv
, cNode
, &change
);
8480 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8481 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8482 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8483 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8484 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8485 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8486 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8487 cData
->max
= str
? atoi(str
) : 0;
8488 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8489 cData
->greeting
= str
? strdup(str
) : NULL
;
8490 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8491 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8492 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8493 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8494 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8495 cData
->topic
= str
? strdup(str
) : NULL
;
8497 if(!IsSuspended(cData
)
8498 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8499 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8500 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8501 cData
->modes
= *modes
;
8503 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8504 if(cData
->modes
.argc
> 1)
8505 cData
->modes
.argc
= 1;
8506 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8507 mod_chanmode_free(modes
);
8510 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8511 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8512 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8514 if(!cData
->users
&& !IsProtected(cData
))
8516 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8517 unregister_channel(cData
, "has empty user list.");
8521 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8522 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8523 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8525 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8526 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8528 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8529 struct record_data
*rd
= iter_data(it
);
8530 const char *note
, *setter
;
8532 if(rd
->type
!= RECDB_OBJECT
)
8534 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8538 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8540 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8542 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8546 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8547 if(!setter
) setter
= "<unknown>";
8548 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8556 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8558 const char *setter
, *reason
, *str
;
8559 struct do_not_register
*dnr
;
8561 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8564 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8567 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8570 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8573 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8576 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8578 dnr
->set
= atoi(str
);
8584 chanserv_version_read(struct dict
*section
)
8588 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8590 chanserv_read_version
= atoi(str
);
8591 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8595 chanserv_saxdb_read(struct dict
*database
)
8597 struct dict
*section
;
8600 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8601 chanserv_version_read(section
);
8603 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8604 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8605 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8607 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8608 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8609 chanserv_channel_read(iter_key(it
), iter_data(it
));
8611 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8612 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8613 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8619 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8621 int high_present
= 0;
8622 saxdb_start_record(ctx
, KEY_USERS
, 1);
8623 for(; uData
; uData
= uData
->next
)
8625 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8627 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8628 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8629 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8630 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
8631 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
8632 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
8634 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8636 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8638 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8639 saxdb_end_record(ctx
);
8641 saxdb_end_record(ctx
);
8642 return high_present
;
8646 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8650 saxdb_start_record(ctx
, KEY_BANS
, 1);
8651 for(; bData
; bData
= bData
->next
)
8653 saxdb_start_record(ctx
, bData
->mask
, 0);
8654 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8655 if(bData
->triggered
)
8656 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8658 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8660 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8662 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8663 saxdb_end_record(ctx
);
8665 saxdb_end_record(ctx
);
8669 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8671 saxdb_start_record(ctx
, name
, 0);
8672 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8673 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8675 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8677 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8679 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8681 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8682 saxdb_end_record(ctx
);
8686 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8688 saxdb_start_record(ctx
, name
, 0);
8689 if(giveownership
->staff_issuer
)
8690 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8691 if(giveownership
->old_owner
)
8692 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8693 if(giveownership
->target
)
8694 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8695 if(giveownership
->target_access
)
8696 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8697 if(giveownership
->reason
)
8698 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8699 if(giveownership
->issued
)
8700 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8701 if(giveownership
->previous
)
8702 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8703 saxdb_end_record(ctx
);
8707 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8711 enum levelOption lvlOpt
;
8712 enum charOption chOpt
;
8714 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8716 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8717 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8719 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8720 if(channel
->registrar
)
8721 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8722 if(channel
->greeting
)
8723 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8724 if(channel
->user_greeting
)
8725 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8726 if(channel
->topic_mask
)
8727 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8728 if(channel
->suspended
)
8729 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8730 if(channel
->giveownership
)
8731 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8733 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8734 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8735 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8736 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8737 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8739 buf
[0] = channel
->chOpts
[chOpt
];
8741 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8743 saxdb_end_record(ctx
);
8745 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8747 mod_chanmode_format(&channel
->modes
, buf
);
8748 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8751 high_present
= chanserv_write_users(ctx
, channel
->users
);
8752 chanserv_write_bans(ctx
, channel
->bans
);
8754 if(dict_size(channel
->notes
))
8758 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8759 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8761 struct note
*note
= iter_data(it
);
8762 saxdb_start_record(ctx
, iter_key(it
), 0);
8763 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8764 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8765 saxdb_end_record(ctx
);
8767 saxdb_end_record(ctx
);
8770 if(channel
->ownerTransfer
)
8771 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8772 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8773 saxdb_end_record(ctx
);
8777 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8781 saxdb_start_record(ctx
, ntype
->name
, 0);
8782 switch(ntype
->set_access_type
)
8784 case NOTE_SET_CHANNEL_ACCESS
:
8785 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8787 case NOTE_SET_CHANNEL_SETTER
:
8788 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8790 case NOTE_SET_PRIVILEGED
: default:
8791 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8794 switch(ntype
->visible_type
)
8796 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8797 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8798 case NOTE_VIS_PRIVILEGED
:
8799 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8801 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8802 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8803 saxdb_end_record(ctx
);
8807 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8809 struct do_not_register
*dnr
;
8812 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8814 dnr
= iter_data(it
);
8815 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8817 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8818 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8819 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8820 saxdb_end_record(ctx
);
8825 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8828 struct chanData
*channel
;
8830 /* Version Control*/
8831 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8832 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8833 saxdb_end_record(ctx
);
8836 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8837 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8838 chanserv_write_note_type(ctx
, iter_data(it
));
8839 saxdb_end_record(ctx
);
8842 saxdb_start_record(ctx
, KEY_DNR
, 1);
8843 write_dnrs_helper(ctx
, handle_dnrs
);
8844 write_dnrs_helper(ctx
, plain_dnrs
);
8845 write_dnrs_helper(ctx
, mask_dnrs
);
8846 saxdb_end_record(ctx
);
8849 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8850 for(channel
= channelList
; channel
; channel
= channel
->next
)
8851 chanserv_write_channel(ctx
, channel
);
8852 saxdb_end_record(ctx
);
8858 chanserv_db_cleanup(void) {
8860 unreg_part_func(handle_part
);
8862 unregister_channel(channelList
, "terminating.");
8863 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8864 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8865 free(chanserv_conf
.support_channels
.list
);
8866 dict_delete(handle_dnrs
);
8867 dict_delete(plain_dnrs
);
8868 dict_delete(mask_dnrs
);
8869 dict_delete(note_types
);
8870 free_string_list(chanserv_conf
.eightball
);
8871 free_string_list(chanserv_conf
.old_ban_names
);
8872 free_string_list(chanserv_conf
.set_shows
);
8873 free(set_shows_list
.list
);
8874 free(uset_shows_list
.list
);
8877 struct userData
*helper
= helperList
;
8878 helperList
= helperList
->next
;
8883 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8884 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8885 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8888 init_chanserv(const char *nick
)
8890 struct chanNode
*chan
;
8892 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8893 conf_register_reload(chanserv_conf_read
);
8895 reg_server_link_func(handle_server_link
);
8897 reg_new_channel_func(handle_new_channel
);
8898 reg_join_func(handle_join
);
8899 reg_part_func(handle_part
);
8900 reg_kick_func(handle_kick
);
8901 reg_topic_func(handle_topic
);
8902 reg_mode_change_func(handle_mode
);
8903 reg_nick_change_func(handle_nick_change
);
8905 reg_auth_func(handle_auth
);
8906 reg_handle_rename_func(handle_rename
);
8907 reg_unreg_func(handle_unreg
);
8909 handle_dnrs
= dict_new();
8910 dict_set_free_data(handle_dnrs
, free
);
8911 plain_dnrs
= dict_new();
8912 dict_set_free_data(plain_dnrs
, free
);
8913 mask_dnrs
= dict_new();
8914 dict_set_free_data(mask_dnrs
, free
);
8916 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8917 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8918 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8919 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8920 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8921 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8922 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8923 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8924 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8925 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8927 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8929 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8930 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8932 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8933 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8934 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8935 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8936 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8938 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8939 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8940 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8941 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8942 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8943 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8945 DEFINE_COMMAND(levels
, 1, 0, NULL
);
8947 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8948 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8949 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8950 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8952 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8953 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8954 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8955 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8956 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8957 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8958 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8959 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8960 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8961 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8963 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8964 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8965 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8966 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8967 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8968 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8969 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8970 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8971 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8972 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8973 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8974 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8975 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8976 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8978 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8979 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8980 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8981 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8983 /* if you change dellamer access, see also places
8984 * like unbanme which have manager hardcoded. */
8985 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8986 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8988 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8990 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8992 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8993 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8994 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8995 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8996 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8997 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8998 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8999 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9000 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9001 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9002 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9003 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9005 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
9006 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
9008 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
9009 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
9010 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
9011 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
9013 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9014 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9015 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
9016 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
9017 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
9019 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9020 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9021 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9022 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9023 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9024 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9025 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9026 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9028 /* Channel options */
9029 DEFINE_CHANNEL_OPTION(defaulttopic
);
9030 DEFINE_CHANNEL_OPTION(topicmask
);
9031 DEFINE_CHANNEL_OPTION(greeting
);
9032 DEFINE_CHANNEL_OPTION(usergreeting
);
9033 DEFINE_CHANNEL_OPTION(modes
);
9034 DEFINE_CHANNEL_OPTION(enfops
);
9035 DEFINE_CHANNEL_OPTION(enfhalfops
);
9036 DEFINE_CHANNEL_OPTION(automode
);
9037 DEFINE_CHANNEL_OPTION(protect
);
9038 DEFINE_CHANNEL_OPTION(enfmodes
);
9039 DEFINE_CHANNEL_OPTION(enftopic
);
9040 DEFINE_CHANNEL_OPTION(pubcmd
);
9041 DEFINE_CHANNEL_OPTION(userinfo
);
9042 DEFINE_CHANNEL_OPTION(dynlimit
);
9043 DEFINE_CHANNEL_OPTION(topicsnarf
);
9044 DEFINE_CHANNEL_OPTION(nodelete
);
9045 DEFINE_CHANNEL_OPTION(toys
);
9046 DEFINE_CHANNEL_OPTION(setters
);
9047 DEFINE_CHANNEL_OPTION(topicrefresh
);
9048 DEFINE_CHANNEL_OPTION(resync
);
9049 DEFINE_CHANNEL_OPTION(ctcpreaction
);
9050 DEFINE_CHANNEL_OPTION(bantimeout
);
9051 DEFINE_CHANNEL_OPTION(inviteme
);
9053 DEFINE_CHANNEL_OPTION(offchannel
);
9054 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
9056 /* Alias set topic to set defaulttopic for compatibility. */
9057 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
9060 DEFINE_USER_OPTION(autoinvite
);
9061 DEFINE_USER_OPTION(autojoin
);
9062 DEFINE_USER_OPTION(info
);
9063 DEFINE_USER_OPTION(autoop
);
9065 /* Alias uset autovoice to uset autoop. */
9066 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
9068 note_types
= dict_new();
9069 dict_set_free_data(note_types
, chanserv_deref_note_type
);
9072 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
9073 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
9074 service_register(chanserv
)->trigger
= '!';
9075 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
9078 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
9080 if(chanserv_conf
.channel_expire_frequency
)
9081 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
9083 if(chanserv_conf
.ban_timeout_frequency
)
9084 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
9086 if(chanserv_conf
.refresh_period
)
9088 time_t next_refresh
;
9089 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
9090 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
9091 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
9094 if (autojoin_channels
&& chanserv
) {
9095 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
9096 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
9097 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
9101 reg_exit_func(chanserv_db_cleanup
);
9102 message_register_table(msgtab
);