1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_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"
114 #define KEY_MAXSETINFO "maxsetinfo"
117 #define KEY_LEVEL "level"
118 #define KEY_INFO "info"
119 #define KEY_SEEN "seen"
120 #define KEY_ACCESSEXPIRY "accessexpiry"
121 #define KEY_CLVLEXPIRY "clvlexpiry"
122 #define KEY_LASTLEVEL "lastlevel"
125 #define KEY_OWNER "owner"
126 #define KEY_REASON "reason"
127 #define KEY_SET "set"
128 #define KEY_DURATION "duration"
129 #define KEY_EXPIRES "expires"
130 #define KEY_TRIGGERED "triggered"
132 #define KEY_GOD_TIMEOUT "god_timeout"
134 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
135 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
137 /* Administrative messages */
138 static const struct message_entry msgtab
[] = {
139 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
141 /* Channel registration */
142 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
143 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
144 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
145 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
146 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
147 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
148 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
149 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
151 /* Do-not-register channels */
152 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
153 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
154 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
155 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
156 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
157 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
158 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
159 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
160 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
161 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
162 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
164 /* Channel unregistration */
165 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
166 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
167 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
168 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
171 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
172 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
174 /* Channel merging */
175 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
176 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
177 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
178 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
179 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
181 /* Handle unregistration */
182 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
185 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
186 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
187 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
188 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
189 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
190 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
191 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
192 { "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." },
193 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
194 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
195 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
196 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
197 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
198 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
200 /* Removing yourself from a channel. */
201 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
202 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
203 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
205 /* User management */
206 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
207 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
208 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
209 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
210 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
211 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
212 { "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." },
213 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
214 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
215 { "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." },
216 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
217 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
218 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
219 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
220 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
221 { "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" },
222 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
224 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
225 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
226 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
227 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
228 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
229 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
230 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
231 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
234 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
235 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
236 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
237 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
238 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
239 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
240 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
241 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
242 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
243 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
244 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
245 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
246 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
247 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
248 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
249 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
250 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
252 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
254 /* Channel management */
255 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
256 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
257 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
259 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
260 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
261 { "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" },
262 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
263 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
264 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
265 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
267 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
268 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
269 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
270 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
271 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
272 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
273 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
274 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
275 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
276 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
277 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
278 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
279 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
280 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
281 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
282 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
283 { "CSMSG_SET_MODES", "$bModes $b %s" },
284 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
285 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
286 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
287 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
288 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
289 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
290 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
291 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
292 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
293 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
294 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
295 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
296 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
297 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
298 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
299 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
300 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
301 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
302 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
303 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
305 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
306 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
307 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
308 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
309 { "CSMSG_USET_INFO", "$bInfo $b %s" },
311 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
312 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
313 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
314 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
315 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
316 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
317 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
318 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
319 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
320 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
321 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
323 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
324 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
325 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
326 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
327 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
328 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
329 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
331 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
332 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
333 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
334 { "CSMSG_PROTECT_NONE", "No users will be protected." },
335 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
336 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
337 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
339 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
340 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
341 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
342 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
343 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
345 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
346 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
347 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
348 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
349 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
351 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
352 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
353 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
354 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
355 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
357 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
358 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
359 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
360 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
361 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
362 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
364 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
365 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
366 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
367 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
368 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
369 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
370 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
371 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
372 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
374 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
375 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
376 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
378 /* Channel userlist */
379 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
380 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
381 /* uncomment if needed to adujust styles (and change code below)
382 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
383 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
384 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
385 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
386 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
387 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
389 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
390 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
391 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
393 /* Channel note list */
394 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
395 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
396 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
397 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
398 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
399 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
400 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
401 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
402 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
403 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
404 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
405 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
406 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
407 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
408 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
410 /* Channel [un]suspension */
411 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
412 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
413 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
414 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
415 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
416 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
417 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
419 /* Access information */
420 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
421 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
422 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
423 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
424 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
425 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
426 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
427 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
428 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
429 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
430 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
432 /* Seen information */
433 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
434 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
435 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
436 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
438 /* Names information */
439 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
440 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
442 /* Channel information */
443 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
444 { "CSMSG_BAR", "----------------------------------------"},
445 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
446 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
447 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
448 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
449 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
450 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
451 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
452 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
453 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
454 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
455 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
456 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
457 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
458 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
459 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
460 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
461 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
462 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
463 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
464 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
465 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
466 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
467 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
468 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
469 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
470 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
472 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
473 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
474 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
475 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
476 { "CSMSG_PEEK_OPS", "$bOps:$b" },
477 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
478 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
480 /* Network information */
481 { "CSMSG_NETWORK_INFO", "Network Information:" },
482 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
483 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
484 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
485 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
486 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
487 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
488 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
489 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
492 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
493 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
494 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
496 /* Channel searches */
497 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
498 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
499 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
500 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
502 /* Channel configuration */
503 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
504 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
505 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
506 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
509 { "CSMSG_USER_OPTIONS", "User Options:" },
510 // { "CSMSG_USER_PROTECTED", "That user is protected." },
513 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
514 { "CSMSG_PING_RESPONSE", "Pong!" },
515 { "CSMSG_WUT_RESPONSE", "wut" },
516 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
517 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
518 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
519 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
520 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
521 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
522 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
525 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
526 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
527 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
528 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
532 /* eject_user and unban_user flags */
533 #define ACTION_KICK 0x0001
534 #define ACTION_BAN 0x0002
535 #define ACTION_ADD_LAMER 0x0004
536 #define ACTION_ADD_TIMED_LAMER 0x0008
537 #define ACTION_UNBAN 0x0010
538 #define ACTION_DEL_LAMER 0x0020
540 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
541 #define MODELEN 40 + KEYLEN
545 #define CSFUNC_ARGS user, channel, argc, argv, cmd
547 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
548 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
549 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
550 reply("MSG_MISSING_PARAMS", argv[0]); \
554 DECLARE_LIST(dnrList
, struct do_not_register
*);
555 DEFINE_LIST(dnrList
, struct do_not_register
*);
557 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
559 struct userNode
*chanserv
;
562 extern struct string_list
*autojoin_channels
;
563 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
564 static struct log_type
*CS_LOG
;
565 struct adduserPending
* adduser_pendings
= NULL
;
566 unsigned int adduser_pendings_count
= 0;
567 unsigned long god_timeout
;
571 struct channelList support_channels
;
572 struct mod_chanmode default_modes
;
574 unsigned long db_backup_frequency
;
575 unsigned long channel_expire_frequency
;
576 unsigned long ban_timeout_frequency
;
579 unsigned int adjust_delay
;
580 long channel_expire_delay
;
581 unsigned int nodelete_level
;
583 unsigned int adjust_threshold
;
584 int join_flood_threshold
;
586 unsigned int greeting_length
;
587 unsigned int refresh_period
;
588 unsigned int giveownership_period
;
590 unsigned int max_owned
;
591 unsigned int max_chan_users
;
592 unsigned int max_chan_bans
; /* lamers */
593 unsigned int max_userinfo_length
;
595 struct string_list
*set_shows
;
596 struct string_list
*eightball
;
597 struct string_list
*old_ban_names
;
599 const char *ctcp_short_ban_duration
;
600 const char *ctcp_long_ban_duration
;
602 const char *irc_operator_epithet
;
603 const char *network_helper_epithet
;
604 const char *support_helper_epithet
;
609 struct userNode
*user
;
610 struct userNode
*bot
;
611 struct chanNode
*channel
;
613 unsigned short lowest
;
614 unsigned short highest
;
615 struct userData
**users
;
616 struct helpfile_table table
;
619 enum note_access_type
621 NOTE_SET_CHANNEL_ACCESS
,
622 NOTE_SET_CHANNEL_SETTER
,
626 enum note_visible_type
629 NOTE_VIS_CHANNEL_USERS
,
635 enum note_access_type set_access_type
;
637 unsigned int min_opserv
;
638 unsigned short min_ulevel
;
640 enum note_visible_type visible_type
;
641 unsigned int max_length
;
648 struct note_type
*type
;
649 char setter
[NICKSERV_HANDLE_LEN
+1];
653 static unsigned int registered_channels
;
654 static unsigned int banCount
;
656 static const struct {
659 unsigned short level
;
661 } accessLevels
[] = { /* MUST be orderd less to most! */
662 { "peon", "Peon", UL_PEON
, '+' },
663 { "halfop", "HalfOp", UL_HALFOP
, '%' },
664 { "op", "Op", UL_OP
, '@' },
665 { "manager", "Manager", UL_MANAGER
, '%' },
666 { "coowner", "Coowner", UL_COOWNER
, '*' },
667 { "owner", "Owner", UL_OWNER
, '!' },
668 { "helper", "BUG:", UL_HELPER
, 'X' }
671 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
672 static const struct {
675 unsigned short default_value
;
676 unsigned int old_idx
;
677 unsigned int old_flag
;
678 unsigned short flag_value
;
680 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
681 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
682 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
683 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
684 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
685 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
686 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
687 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
688 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
691 struct charOptionValues
{
694 } automodeValues
[] = {
695 { 'n', "CSMSG_AUTOMODE_NONE" },
696 { 'y', "CSMSG_AUTOMODE_NORMAL" },
697 { 'v', "CSMSG_AUTOMODE_VOICE" },
698 { 'h', "CSMSG_AUTOMODE_HOP" },
699 { 'o', "CSMSG_AUTOMODE_OP" },
700 { 'm', "CSMSG_AUTOMODE_MUTE" },
701 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
702 }, protectValues
[] = {
703 { 'a', "CSMSG_PROTECT_ALL" },
704 { 'e', "CSMSG_PROTECT_EQUAL" },
705 { 'l', "CSMSG_PROTECT_LOWER" },
706 { 'n', "CSMSG_PROTECT_NONE" }
708 { 'd', "CSMSG_TOYS_DISABLED" },
709 { 'n', "CSMSG_TOYS_PRIVATE" },
710 { 'p', "CSMSG_TOYS_PUBLIC" }
711 }, topicRefreshValues
[] = {
712 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
713 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
714 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
715 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
716 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
717 }, ctcpReactionValues
[] = {
718 { 'n', "CSMSG_CTCPREACTION_NONE" },
719 { 'k', "CSMSG_CTCPREACTION_KICK" },
720 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
721 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
722 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
723 }, banTimeoutValues
[] = {
724 { '0', "CSMSG_BANTIMEOUT_NONE" },
725 { '1', "CSMSG_BANTIMEOUT_10M" },
726 { '2', "CSMSG_BANTIMEOUT_2H" },
727 { '3', "CSMSG_BANTIMEOUT_4H" },
728 { '4', "CSMSG_BANTIMEOUT_1D" },
729 { '5', "CSMSG_BANTIMEOUT_1W" }
732 { 'n', "CSMSG_RESYNC_NEVER" },
733 { '1', "CSMSG_RESYNC_3_HOURS" },
734 { '2', "CSMSG_RESYNC_6_HOURS" },
735 { '3', "CSMSG_RESYNC_12_HOURS" },
736 { '4', "CSMSG_RESYNC_24_HOURS" }
739 static const struct {
743 unsigned int old_idx
;
745 struct charOptionValues
*values
;
747 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
748 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
749 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
750 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
751 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
752 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
753 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
756 struct userData
*helperList
;
757 struct chanData
*channelList
;
758 static struct module *chanserv_module
;
759 static unsigned int userCount
;
760 unsigned int chanserv_read_version
= 0; /* db version control */
762 #define CHANSERV_DB_VERSION 2
764 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
765 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
768 user_level_from_name(const char *name
, unsigned short clamp_level
)
770 unsigned int level
= 0, ii
;
772 level
= strtoul(name
, NULL
, 10);
773 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
774 if(!irccasecmp(name
, accessLevels
[ii
].name
))
775 level
= accessLevels
[ii
].level
;
776 if(level
> clamp_level
)
782 user_level_name_from_level(int level
)
790 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
791 if(level
>= accessLevels
[ii
].level
)
792 highest
= accessLevels
[ii
].title
;
798 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
801 *minl
= strtoul(arg
, &sep
, 10);
809 *maxl
= strtoul(sep
+1, &sep
, 10);
817 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
819 struct userData
*uData
, **head
;
821 if(!channel
|| !handle
)
824 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
825 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
827 for(uData
= helperList
;
828 uData
&& uData
->handle
!= handle
;
829 uData
= uData
->next
);
833 uData
= calloc(1, sizeof(struct userData
));
834 uData
->handle
= handle
;
836 uData
->access
= UL_HELPER
;
842 uData
->next
= helperList
;
844 helperList
->prev
= uData
;
852 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
853 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
856 head
= &(channel
->users
);
859 if(uData
&& (uData
!= *head
))
861 /* Shuffle the user to the head of whatever list he was in. */
863 uData
->next
->prev
= uData
->prev
;
865 uData
->prev
->next
= uData
->next
;
871 (**head
).prev
= uData
;
878 /* Returns non-zero if user has at least the minimum access.
879 * exempt_owner is set when handling !set, so the owner can set things
882 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
884 struct userData
*uData
;
885 struct chanData
*cData
= channel
->channel_info
;
886 unsigned short minimum
= cData
->lvlOpts
[opt
];
889 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
892 if(minimum
<= uData
->access
)
894 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
899 /* Scan for other users authenticated to the same handle
900 still in the channel. If so, keep them listed as present.
902 user is optional, if not null, it skips checking that userNode
903 (for the handle_part function) */
905 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
909 if(IsSuspended(uData
->channel
)
910 || IsUserSuspended(uData
)
911 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
923 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
925 unsigned int eflags
, argc
;
927 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
929 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
930 if(!channel
->channel_info
931 || IsSuspended(channel
->channel_info
)
933 || !ircncasecmp(text
, "ACTION ", 7))
935 /* We dont punish people we know -Rubin
936 * * Figure out the minimum level needed to CTCP the channel *
938 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
941 /* If they are a user of the channel, they are exempt */
942 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
944 /* We need to enforce against them; do so. */
947 argv
[1] = user
->nick
;
949 if(GetUserMode(channel
, user
))
950 eflags
|= ACTION_KICK
;
951 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
952 default: case 'n': return;
954 eflags
|= ACTION_KICK
;
957 eflags
|= ACTION_BAN
;
960 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
961 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
964 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
965 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
968 argv
[argc
++] = bad_ctcp_reason
;
969 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
973 chanserv_create_note_type(const char *name
)
975 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
976 strcpy(ntype
->name
, name
);
978 dict_insert(note_types
, ntype
->name
, ntype
);
983 chanserv_deref_note_type(void *data
)
985 struct note_type
*ntype
= data
;
987 if(--ntype
->refs
> 0)
993 chanserv_flush_note_type(struct note_type
*ntype
)
995 struct chanData
*cData
;
996 for(cData
= channelList
; cData
; cData
= cData
->next
)
997 dict_remove(cData
->notes
, ntype
->name
);
1001 chanserv_truncate_notes(struct note_type
*ntype
)
1003 struct chanData
*cData
;
1005 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1007 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1008 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1011 if(strlen(note
->note
) <= ntype
->max_length
)
1013 dict_remove2(cData
->notes
, ntype
->name
, 1);
1014 note
= realloc(note
, size
);
1015 note
->note
[ntype
->max_length
] = 0;
1016 dict_insert(cData
->notes
, ntype
->name
, note
);
1020 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1022 static struct note
*
1023 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1026 unsigned int len
= strlen(text
);
1028 if(len
> type
->max_length
) len
= type
->max_length
;
1029 note
= calloc(1, sizeof(*note
) + len
);
1031 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1032 memcpy(note
->note
, text
, len
);
1033 note
->note
[len
] = 0;
1034 dict_insert(channel
->notes
, type
->name
, note
);
1040 chanserv_free_note(void *data
)
1042 struct note
*note
= data
;
1044 chanserv_deref_note_type(note
->type
);
1045 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1049 static MODCMD_FUNC(cmd_createnote
) {
1050 struct note_type
*ntype
;
1051 unsigned int arg
= 1, existed
= 0, max_length
;
1053 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1056 ntype
= chanserv_create_note_type(argv
[arg
]);
1057 if(!irccasecmp(argv
[++arg
], "privileged"))
1060 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1061 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1063 else if(!irccasecmp(argv
[arg
], "channel"))
1065 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1068 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1071 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1072 ntype
->set_access
.min_ulevel
= ulvl
;
1074 else if(!irccasecmp(argv
[arg
], "setter"))
1076 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1080 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1084 if(!irccasecmp(argv
[++arg
], "privileged"))
1085 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1086 else if(!irccasecmp(argv
[arg
], "channel_users"))
1087 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1088 else if(!irccasecmp(argv
[arg
], "all"))
1089 ntype
->visible_type
= NOTE_VIS_ALL
;
1091 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1095 if((arg
+1) >= argc
) {
1096 reply("MSG_MISSING_PARAMS", argv
[0]);
1099 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1100 if(max_length
< 20 || max_length
> 450)
1102 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1105 if(existed
&& (max_length
< ntype
->max_length
))
1107 ntype
->max_length
= max_length
;
1108 chanserv_truncate_notes(ntype
);
1110 ntype
->max_length
= max_length
;
1113 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1115 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1120 dict_remove(note_types
, ntype
->name
);
1124 static MODCMD_FUNC(cmd_removenote
) {
1125 struct note_type
*ntype
;
1128 ntype
= dict_find(note_types
, argv
[1], NULL
);
1129 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1132 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1139 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1142 chanserv_flush_note_type(ntype
);
1144 dict_remove(note_types
, argv
[1]);
1145 reply("CSMSG_NOTE_DELETED", argv
[1]);
1150 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1154 if(orig
->modes_set
& change
->modes_clear
)
1156 if(orig
->modes_clear
& change
->modes_set
)
1158 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1159 && strcmp(orig
->new_key
, change
->new_key
))
1161 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1162 && (orig
->new_limit
!= change
->new_limit
))
1167 static char max_length_text
[MAXLEN
+1][16];
1169 static struct helpfile_expansion
1170 chanserv_expand_variable(const char *variable
)
1172 struct helpfile_expansion exp
;
1174 if(!irccasecmp(variable
, "notes"))
1177 exp
.type
= HF_TABLE
;
1178 exp
.value
.table
.length
= 1;
1179 exp
.value
.table
.width
= 3;
1180 exp
.value
.table
.flags
= 0;
1181 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1182 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1183 exp
.value
.table
.contents
[0][0] = "Note Type";
1184 exp
.value
.table
.contents
[0][1] = "Visibility";
1185 exp
.value
.table
.contents
[0][2] = "Max Length";
1186 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1188 struct note_type
*ntype
= iter_data(it
);
1191 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1192 row
= exp
.value
.table
.length
++;
1193 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1194 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1195 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1196 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1198 if(!max_length_text
[ntype
->max_length
][0])
1199 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1200 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1205 exp
.type
= HF_STRING
;
1206 exp
.value
.str
= NULL
;
1210 static struct chanData
*
1211 register_channel(struct chanNode
*cNode
, char *registrar
)
1213 struct chanData
*channel
;
1214 enum levelOption lvlOpt
;
1215 enum charOption chOpt
;
1217 channel
= calloc(1, sizeof(struct chanData
));
1219 channel
->notes
= dict_new();
1220 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1222 channel
->registrar
= strdup(registrar
);
1223 channel
->registered
= now
;
1224 channel
->visited
= now
;
1225 channel
->limitAdjusted
= now
;
1226 channel
->ownerTransfer
= now
;
1227 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1228 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1229 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1230 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1231 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1233 channel
->prev
= NULL
;
1234 channel
->next
= channelList
;
1237 channelList
->prev
= channel
;
1238 channelList
= channel
;
1239 registered_channels
++;
1241 channel
->channel
= cNode
;
1243 cNode
->channel_info
= channel
;
1248 static struct userData
*
1249 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
, time_t accessexpiry
)
1251 struct userData
*ud
;
1253 if(access
> UL_OWNER
)
1256 ud
= calloc(1, sizeof(*ud
));
1257 ud
->channel
= channel
;
1258 ud
->handle
= handle
;
1260 ud
->access
= access
;
1261 ud
->info
= info
? strdup(info
) : NULL
;
1262 ud
->accessexpiry
= accessexpiry
? accessexpiry
: 0;
1267 ud
->next
= channel
->users
;
1269 channel
->users
->prev
= ud
;
1270 channel
->users
= ud
;
1272 channel
->userCount
++;
1276 ud
->u_next
= ud
->handle
->channels
;
1278 ud
->u_next
->u_prev
= ud
;
1279 ud
->handle
->channels
= ud
;
1281 ud
->flags
= USER_FLAGS_DEFAULT
;
1285 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1288 chanserv_expire_tempuser(void *data
)
1290 struct userData
*uData
= data
;
1294 handle
= strdup(uData
->handle
->handle
);
1295 if (uData
->accessexpiry
> 0) {
1296 if (uData
->present
) {
1297 struct userNode
*user
, *next_un
= NULL
;
1298 struct handle_info
*hi
;
1300 hi
= get_handle_info(handle
);
1301 for (user
= hi
->users
; user
; user
= next_un
) {
1302 struct mod_chanmode
*change
;
1303 struct modeNode
*mn
;
1304 unsigned int count
= 0;
1306 send_message(user
, chanserv
, "CSMSG_AUTO_DELETED", chanserv
->nick
, uData
->channel
->channel
->name
);
1307 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)) {
1308 next_un
= user
->next_authed
;
1312 change
= mod_chanmode_alloc(2);
1313 change
->args
[count
].mode
= MODE_REMOVE
| MODE_CHANOP
;
1314 change
->args
[count
++].u
.member
= mn
;
1317 change
->argc
= count
;
1318 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1320 mod_chanmode_free(change
);
1321 next_un
= user
->next_authed
;
1324 del_channel_user(uData
, 1);
1330 chanserv_expire_tempclvl(void *data
)
1332 struct userData
*uData
= data
;
1336 handle
= strdup(uData
->handle
->handle
);
1337 if (uData
->clvlexpiry
> 0) {
1338 int changemodes
= 0;
1339 unsigned int mode
= 0;
1341 if (((uData
->lastaccess
== UL_PEON
) || (uData
->lastaccess
== UL_HALFOP
)) && (uData
->access
>= UL_OP
)) {
1343 mode
= MODE_REMOVE
| MODE_CHANOP
;
1344 } else if ((uData
->lastaccess
== UL_PEON
) && (uData
->access
== UL_HALFOP
)) {
1346 mode
= MODE_REMOVE
| MODE_HALFOP
;
1350 if (uData
->present
) {
1351 struct userNode
*user
, *next_un
= NULL
;
1352 struct handle_info
*hi
;
1354 hi
= get_handle_info(handle
);
1355 for (user
= hi
->users
; user
; user
= next_un
) {
1356 struct mod_chanmode
*change
;
1357 struct modeNode
*mn
;
1358 unsigned int count
= 0;
1360 send_message(user
, chanserv
, "CSMSG_CLVL_EXPIRED", uData
->channel
->channel
->name
);
1361 if (!(mn
= GetUserMode(uData
->channel
->channel
, user
))) {
1362 next_un
= user
->next_authed
;
1366 if (changemodes
== 0) {
1367 next_un
= user
->next_authed
;
1371 change
= mod_chanmode_alloc(2);
1372 change
->args
[count
].mode
= mode
;
1373 change
->args
[count
++].u
.member
= mn
;
1376 change
->argc
= count
;
1377 mod_chanmode_announce(chanserv
, uData
->channel
->channel
, change
);
1379 mod_chanmode_free(change
);
1380 next_un
= user
->next_authed
;
1384 uData
->access
= uData
->lastaccess
;
1385 uData
->lastaccess
= 0;
1386 uData
->clvlexpiry
= 0;
1392 del_channel_user(struct userData
*user
, int do_gc
)
1394 struct chanData
*channel
= user
->channel
;
1396 channel
->userCount
--;
1399 timeq_del(0, chanserv_expire_tempuser
, user
, TIMEQ_IGNORE_WHEN
);
1400 timeq_del(0, chanserv_expire_tempclvl
, user
, TIMEQ_IGNORE_WHEN
);
1403 user
->prev
->next
= user
->next
;
1405 channel
->users
= user
->next
;
1407 user
->next
->prev
= user
->prev
;
1410 user
->u_prev
->u_next
= user
->u_next
;
1412 user
->handle
->channels
= user
->u_next
;
1414 user
->u_next
->u_prev
= user
->u_prev
;
1418 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1419 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1420 unregister_channel(channel
, "lost all users.");
1424 static struct adduserPending
*
1425 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1427 struct adduserPending
*ap
;
1428 ap
= calloc(1,sizeof(struct adduserPending
));
1429 ap
->channel
= channel
;
1432 ap
->created
= time(NULL
);
1434 /* ap->prev defaults to NULL already.. */
1435 ap
->next
= adduser_pendings
;
1436 if(adduser_pendings
)
1437 adduser_pendings
->prev
= ap
;
1438 adduser_pendings
= ap
;
1439 adduser_pendings_count
++;
1444 del_adduser_pending(struct adduserPending
*ap
)
1447 ap
->prev
->next
= ap
->next
;
1449 adduser_pendings
= ap
->next
;
1452 ap
->next
->prev
= ap
->prev
;
1456 static void expire_adduser_pending();
1458 /* find_adduser_pending(channel, user) will find an arbitrary record
1459 * from user, channel, or user and channel.
1460 * if user or channel are NULL, they will match any records.
1462 static struct adduserPending
*
1463 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1465 struct adduserPending
*ap
;
1467 expire_adduser_pending(); /* why not here.. */
1469 if(!channel
&& !user
) /* 2 nulls matches all */
1470 return(adduser_pendings
);
1471 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1473 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1480 /* Remove all pendings for a user or channel
1482 * called in nickserv.c DelUser() and proto-* unregister_channel()
1485 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1487 struct adduserPending
*ap
;
1489 /* So this is a bit wastefull, i hate dealing with linked lists.
1490 * if its a problem we'll rewrite it right */
1491 while((ap
= find_adduser_pending(channel
, user
))) {
1492 del_adduser_pending(ap
);
1496 /* Called from nickserv.c cmd_auth after someone auths */
1498 process_adduser_pending(struct userNode
*user
)
1500 struct adduserPending
*ap
;
1501 if(!user
->handle_info
)
1502 return; /* not associated with an account */
1503 while((ap
= find_adduser_pending(NULL
, user
)))
1505 struct userData
*actee
;
1506 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1508 /* Already on the userlist. do nothing*/
1512 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
, 0);
1513 scan_user_presence(actee
, NULL
);
1515 del_adduser_pending(ap
);
1520 expire_adduser_pending()
1522 struct adduserPending
*ap
, *ap_next
;
1523 ap
= adduser_pendings
;
1526 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1528 ap_next
= ap
->next
; /* save next */
1529 del_adduser_pending(ap
); /* free and relink */
1530 ap
= ap_next
; /* advance */
1537 static void expire_ban(void *data
);
1540 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1543 unsigned int ii
, l1
, l2
;
1548 bd
= malloc(sizeof(struct banData
));
1550 bd
->channel
= channel
;
1552 bd
->triggered
= triggered
;
1553 bd
->expires
= expires
;
1555 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1557 extern const char *hidden_host_suffix
;
1558 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1562 l2
= strlen(old_name
);
1565 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1567 new_mask
= alloca(MAXLEN
);
1568 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1571 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1573 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1574 bd
->reason
= strdup(reason
);
1577 timeq_add(expires
, expire_ban
, bd
);
1580 bd
->next
= channel
->bans
; /* lamers */
1582 channel
->bans
->prev
= bd
;
1584 channel
->banCount
++;
1591 del_channel_ban(struct banData
*ban
)
1593 ban
->channel
->banCount
--;
1597 ban
->prev
->next
= ban
->next
;
1599 ban
->channel
->bans
= ban
->next
;
1602 ban
->next
->prev
= ban
->prev
;
1605 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1614 expire_ban(void *data
) /* lamer.. */
1616 struct banData
*bd
= data
;
1617 if(!IsSuspended(bd
->channel
))
1619 struct banList bans
;
1620 struct mod_chanmode change
;
1622 bans
= bd
->channel
->channel
->banlist
;
1623 mod_chanmode_init(&change
);
1624 for(ii
=0; ii
<bans
.used
; ii
++)
1626 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1629 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1630 change
.args
[0].u
.hostmask
= bd
->mask
;
1631 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1637 del_channel_ban(bd
);
1640 static void chanserv_expire_suspension(void *data
);
1643 unregister_channel(struct chanData
*channel
, const char *reason
)
1645 struct mod_chanmode change
;
1646 char msgbuf
[MAXLEN
];
1648 /* After channel unregistration, the following must be cleaned
1650 - Channel information.
1652 - Channel bans. (lamers)
1653 - Channel suspension data.
1654 - adduser_pending data.
1655 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1661 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1665 mod_chanmode_init(&change
);
1666 change
.modes_clear
|= MODE_REGISTERED
;
1667 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1670 wipe_adduser_pending(channel
->channel
, NULL
);
1672 while(channel
->users
)
1673 del_channel_user(channel
->users
, 0);
1675 while(channel
->bans
)
1676 del_channel_ban(channel
->bans
);
1678 free(channel
->topic
);
1679 free(channel
->registrar
);
1680 free(channel
->greeting
);
1681 free(channel
->user_greeting
);
1682 free(channel
->topic_mask
);
1685 channel
->prev
->next
= channel
->next
;
1687 channelList
= channel
->next
;
1690 channel
->next
->prev
= channel
->prev
;
1692 if(channel
->suspended
)
1694 struct chanNode
*cNode
= channel
->channel
;
1695 struct suspended
*suspended
, *next_suspended
;
1697 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1699 next_suspended
= suspended
->previous
;
1700 free(suspended
->suspender
);
1701 free(suspended
->reason
);
1702 if(suspended
->expires
)
1703 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1708 cNode
->channel_info
= NULL
;
1710 channel
->channel
->channel_info
= NULL
;
1712 dict_delete(channel
->notes
);
1713 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1714 if(!IsSuspended(channel
))
1715 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1716 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1717 UnlockChannel(channel
->channel
);
1719 registered_channels
--;
1723 expire_channels(UNUSED_ARG(void *data
))
1725 struct chanData
*channel
, *next
;
1726 struct userData
*user
;
1727 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1729 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1730 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1732 for(channel
= channelList
; channel
; channel
= next
)
1734 next
= channel
->next
;
1736 /* See if the channel can be expired. */
1737 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1738 || IsProtected(channel
))
1741 /* Make sure there are no high-ranking users still in the channel. */
1742 for(user
=channel
->users
; user
; user
=user
->next
)
1743 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1748 /* Unregister the channel */
1749 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1750 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1751 unregister_channel(channel
, "registration expired.");
1754 if(chanserv_conf
.channel_expire_frequency
)
1755 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1759 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1761 char protect
= channel
->chOpts
[chProtect
];
1762 struct userData
*cs_victim
, *cs_aggressor
;
1764 /* If victim access level is greater than set invitelevel, don't let
1765 * us kick them, but don't consider it punishment if someone else does
1769 if(victim
== aggressor
)
1771 /* Don't protect if the victim isn't authenticated (because they
1772 can't be a channel user), unless we are to protect non-users
1775 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1777 /* If they have enough access to invite themselvs through a ban,
1778 * and its us kicking them, don't. -Rubin */
1779 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1785 if(protect
!= 'a' && !cs_victim
)
1788 /* Protect if the aggressor isn't a user because at this point,
1789 the aggressor can only be less than or equal to the victim. */
1791 /* Not protected from chanserv except above */
1792 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1793 if(aggressor
== chanserv
)
1796 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1800 /* If the aggressor was a user, then the victim can't be helped. */
1807 if(cs_victim
->access
> cs_aggressor
->access
)
1812 if(cs_victim
->access
>= cs_aggressor
->access
)
1821 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1823 struct chanData
*cData
= channel
->channel_info
;
1824 struct userData
*cs_victim
;
1826 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1827 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1828 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1831 reply("CSMSG_OPBY_LOCKED");
1833 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1841 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1843 struct chanData
*cData
= channel
->channel_info
;
1844 struct userData
*cs_victim
;
1846 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1847 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1848 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1850 reply("CSMSG_HOPBY_LOCKED");
1859 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1861 if(IsService(victim
))
1863 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1867 if(protect_user(victim
, user
, channel
->channel_info
, false))
1869 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1877 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1879 if(IsService(victim
))
1881 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1885 if(protect_user(victim
, user
, channel
->channel_info
, false))
1887 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1894 static struct do_not_register
*
1895 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1897 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1898 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1899 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1900 strcpy(dnr
->reason
, reason
);
1902 if(dnr
->chan_name
[0] == '*')
1903 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1904 else if(strpbrk(dnr
->chan_name
, "*?"))
1905 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1907 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1911 static struct dnrList
1912 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1914 struct dnrList list
;
1916 struct do_not_register
*dnr
;
1918 dnrList_init(&list
);
1919 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1920 dnrList_append(&list
, dnr
);
1921 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1922 dnrList_append(&list
, dnr
);
1924 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1925 if(match_ircglob(chan_name
, iter_key(it
)))
1926 dnrList_append(&list
, iter_data(it
));
1931 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1933 struct dnrList list
;
1934 struct do_not_register
*dnr
;
1936 char buf
[INTERVALLEN
];
1938 list
= chanserv_find_dnrs(chan_name
, handle
);
1939 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1941 dnr
= list
.list
[ii
];
1944 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1945 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1948 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1951 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1956 struct do_not_register
*
1957 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1959 struct do_not_register
*dnr
;
1962 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1966 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1968 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1969 if(match_ircglob(chan_name
, iter_key(it
)))
1970 return iter_data(it
);
1975 static CHANSERV_FUNC(cmd_noregister
)
1978 struct do_not_register
*dnr
;
1979 char buf
[INTERVALLEN
];
1980 unsigned int matches
;
1986 reply("CSMSG_DNR_SEARCH_RESULTS");
1987 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1990 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1992 dnr
= iter_data(it
);
1994 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1996 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1999 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
2001 dnr
= iter_data(it
);
2003 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2005 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2008 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
2010 dnr
= iter_data(it
);
2012 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
2014 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
2019 reply("MSG_MATCH_COUNT", matches
);
2021 reply("MSG_NO_MATCHES");
2027 if(!IsChannelName(target
) && (*target
!= '*'))
2029 reply("CSMSG_NOT_DNR", target
);
2035 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
2036 if((*target
== '*') && !get_handle_info(target
+ 1))
2038 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
2041 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
2042 reply("CSMSG_NOREGISTER_CHANNEL", target
);
2046 reply("CSMSG_DNR_SEARCH_RESULTS");
2047 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
2050 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
2052 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
2054 reply("MSG_NO_MATCHES");
2058 static CHANSERV_FUNC(cmd_allowregister
)
2060 const char *chan_name
= argv
[1];
2062 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
2064 dict_remove(handle_dnrs
, chan_name
+1);
2065 reply("CSMSG_DNR_REMOVED", chan_name
);
2067 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
2069 dict_remove(plain_dnrs
, chan_name
);
2070 reply("CSMSG_DNR_REMOVED", chan_name
);
2072 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
2074 dict_remove(mask_dnrs
, chan_name
);
2075 reply("CSMSG_DNR_REMOVED", chan_name
);
2079 reply("CSMSG_NO_SUCH_DNR", chan_name
);
2086 chanserv_get_owned_count(struct handle_info
*hi
)
2088 struct userData
*cList
;
2091 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
2092 if(cList
->access
== UL_OWNER
)
2097 static CHANSERV_FUNC(cmd_register
)
2099 struct handle_info
*handle
;
2100 struct chanData
*cData
;
2101 struct modeNode
*mn
;
2102 char reason
[MAXLEN
];
2104 unsigned int new_channel
, force
=0;
2105 struct do_not_register
*dnr
;
2108 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
2109 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2115 if(channel
->channel_info
)
2117 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2121 if(channel
->bad_channel
)
2123 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2127 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2129 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2134 chan_name
= channel
->name
;
2140 reply("MSG_MISSING_PARAMS", cmd
->name
);
2141 svccmd_send_help_brief(user
, chanserv
, cmd
);
2144 if(!IsChannelName(argv
[1]))
2146 reply("MSG_NOT_CHANNEL_NAME");
2150 if(opserv_bad_channel(argv
[1]))
2152 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2157 chan_name
= argv
[1];
2160 if(argc
>= (new_channel
+2))
2162 if(!IsHelping(user
))
2164 reply("CSMSG_PROXY_FORBIDDEN");
2168 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2170 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2171 dnr
= chanserv_is_dnr(chan_name
, handle
);
2173 /* Check if they are over the limit.. */
2174 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2176 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2183 handle
= user
->handle_info
;
2184 dnr
= chanserv_is_dnr(chan_name
, handle
);
2185 /* Check if they are over the limit.. */
2186 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2188 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2191 /* Check if another service is in the channel */
2193 for(n
= 0; n
< channel
->members
.used
; n
++)
2195 mn
= channel
->members
.list
[n
];
2196 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2198 reply("CSMSG_ANOTHER_SERVICE");
2205 if(!IsHelping(user
))
2206 reply("CSMSG_DNR_CHANNEL", chan_name
);
2208 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2212 /* now handled above for message specilization *
2213 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2215 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2221 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2223 cData
= register_channel(channel
, user
->handle_info
->handle
);
2224 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
, 0), NULL
);
2225 cData
->modes
= chanserv_conf
.default_modes
;
2227 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2228 if (IsOffChannel(cData
))
2230 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2234 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2235 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2236 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2238 mod_chanmode_announce(chanserv
, channel
, change
);
2239 mod_chanmode_free(change
);
2242 /* Initialize the channel's max user record. */
2243 cData
->max
= channel
->members
.used
;
2244 cData
->maxsetinfo
= chanserv_conf
.max_userinfo_length
;
2246 if(handle
!= user
->handle_info
)
2247 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2250 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2251 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2252 handle
->handle
, user
->handle_info
->handle
);
2257 make_confirmation_string(struct userData
*uData
)
2259 static char strbuf
[16];
2264 for(src
= uData
->handle
->handle
; *src
; )
2265 accum
= accum
* 31 + toupper(*src
++);
2267 for(src
= uData
->channel
->channel
->name
; *src
; )
2268 accum
= accum
* 31 + toupper(*src
++);
2269 sprintf(strbuf
, "%08x", accum
);
2273 static CHANSERV_FUNC(cmd_unregister
)
2276 char reason
[MAXLEN
];
2277 struct chanData
*cData
;
2278 struct userData
*uData
;
2280 cData
= channel
->channel_info
;
2283 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2287 uData
= GetChannelUser(cData
, user
->handle_info
);
2288 if(!uData
|| (uData
->access
< UL_OWNER
))
2290 reply("CSMSG_NO_ACCESS");
2294 if(IsProtected(cData
))
2296 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2300 if(!IsHelping(user
))
2302 const char *confirm_string
;
2303 if(IsSuspended(cData
))
2305 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2308 confirm_string
= make_confirmation_string(uData
);
2309 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2311 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2316 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2317 name
= strdup(channel
->name
);
2318 unregister_channel(cData
, reason
);
2319 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2320 reply("CSMSG_UNREG_SUCCESS", name
);
2326 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2328 extern struct userNode
*spamserv
;
2329 struct mod_chanmode
*change
;
2331 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2333 change
= mod_chanmode_alloc(2);
2335 change
->args
[0].mode
= MODE_CHANOP
;
2336 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2337 change
->args
[1].mode
= MODE_CHANOP
;
2338 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2342 change
= mod_chanmode_alloc(1);
2344 change
->args
[0].mode
= MODE_CHANOP
;
2345 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2348 mod_chanmode_announce(chanserv
, channel
, change
);
2349 mod_chanmode_free(change
);
2352 static CHANSERV_FUNC(cmd_move
)
2354 struct mod_chanmode change
;
2355 struct chanNode
*target
;
2356 struct modeNode
*mn
;
2357 struct userData
*uData
;
2358 struct do_not_register
*dnr
;
2359 int chanserv_join
= 0, spamserv_join
;
2363 if(IsProtected(channel
->channel_info
))
2365 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2369 if(!IsChannelName(argv
[1]))
2371 reply("MSG_NOT_CHANNEL_NAME");
2375 if(opserv_bad_channel(argv
[1]))
2377 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2381 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2383 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2385 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2387 if(!IsHelping(user
))
2388 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2390 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2396 mod_chanmode_init(&change
);
2397 if(!(target
= GetChannel(argv
[1])))
2399 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2400 if(!IsSuspended(channel
->channel_info
))
2403 else if(target
->channel_info
)
2405 reply("CSMSG_ALREADY_REGGED", target
->name
);
2408 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2409 && !IsHelping(user
))
2411 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2414 else if(!IsSuspended(channel
->channel_info
))
2419 /* Clear MODE_REGISTERED from old channel, add it to new. */
2421 change
.modes_clear
= MODE_REGISTERED
;
2422 mod_chanmode_announce(chanserv
, channel
, &change
);
2423 change
.modes_clear
= 0;
2424 change
.modes_set
= MODE_REGISTERED
;
2425 mod_chanmode_announce(chanserv
, target
, &change
);
2428 /* Move the channel_info to the target channel; it
2429 shouldn't be necessary to clear timeq callbacks
2430 for the old channel. */
2431 target
->channel_info
= channel
->channel_info
;
2432 target
->channel_info
->channel
= target
;
2433 channel
->channel_info
= NULL
;
2435 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2438 ss_cs_join_channel(target
, spamserv_join
);
2440 if(!IsSuspended(target
->channel_info
))
2442 char reason2
[MAXLEN
];
2443 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2444 DelChannelUser(chanserv
, channel
, reason2
, 0);
2447 UnlockChannel(channel
);
2448 LockChannel(target
);
2449 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2450 channel
->name
, target
->name
, user
->handle_info
->handle
);
2452 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2457 merge_users(struct chanData
*source
, struct chanData
*target
)
2459 struct userData
*suData
, *tuData
, *next
;
2465 /* Insert the source's users into the scratch area. */
2466 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2467 dict_insert(merge
, suData
->handle
->handle
, suData
);
2469 /* Iterate through the target's users, looking for
2470 users common to both channels. The lower access is
2471 removed from either the scratch area or target user
2473 for(tuData
= target
->users
; tuData
; tuData
= next
)
2475 struct userData
*choice
;
2477 next
= tuData
->next
;
2479 /* If a source user exists with the same handle as a target
2480 channel's user, resolve the conflict by removing one. */
2481 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2485 /* Pick the data we want to keep. */
2486 /* If the access is the same, use the later seen time. */
2487 if(suData
->access
== tuData
->access
)
2488 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2489 else /* Otherwise, keep the higher access level. */
2490 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2492 /* Remove the user that wasn't picked. */
2493 if(choice
== tuData
)
2495 dict_remove(merge
, suData
->handle
->handle
);
2496 del_channel_user(suData
, 0);
2499 del_channel_user(tuData
, 0);
2502 /* Move the remaining users to the target channel. */
2503 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2505 suData
= iter_data(it
);
2507 /* Insert the user into the target channel's linked list. */
2508 suData
->prev
= NULL
;
2509 suData
->next
= target
->users
;
2510 suData
->channel
= target
;
2513 target
->users
->prev
= suData
;
2514 target
->users
= suData
;
2516 /* Update the user counts for the target channel; the
2517 source counts are left alone. */
2518 target
->userCount
++;
2521 /* Possible to assert (source->users == NULL) here. */
2522 source
->users
= NULL
;
2527 merge_bans(struct chanData
*source
, struct chanData
*target
)
2529 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2531 /* Hold on to the original head of the target ban list
2532 to avoid comparing source bans with source bans. */
2533 tFront
= target
->bans
;
2535 /* Perform a totally expensive O(n*m) merge, ick. */
2536 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2538 /* Flag to track whether the ban's been moved
2539 to the destination yet. */
2542 /* Possible to assert (sbData->prev == NULL) here. */
2543 sNext
= sbData
->next
;
2545 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2547 tNext
= tbData
->next
;
2549 /* Perform two comparisons between each source
2550 and target ban, conflicts are resolved by
2551 keeping the broader ban and copying the later
2552 expiration and triggered time. */
2553 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2555 /* There is a broader ban in the target channel that
2556 overrides one in the source channel; remove the
2557 source ban and break. */
2558 if(sbData
->expires
> tbData
->expires
)
2559 tbData
->expires
= sbData
->expires
;
2560 if(sbData
->triggered
> tbData
->triggered
)
2561 tbData
->triggered
= sbData
->triggered
;
2562 del_channel_ban(sbData
);
2565 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2567 /* There is a broader ban in the source channel that
2568 overrides one in the target channel; remove the
2569 target ban, fall through and move the source over. */
2570 if(tbData
->expires
> sbData
->expires
)
2571 sbData
->expires
= tbData
->expires
;
2572 if(tbData
->triggered
> sbData
->triggered
)
2573 sbData
->triggered
= tbData
->triggered
;
2574 if(tbData
== tFront
)
2576 del_channel_ban(tbData
);
2579 /* Source bans can override multiple target bans, so
2580 we allow a source to run through this loop multiple
2581 times, but we can only move it once. */
2586 /* Remove the source ban from the source ban list. */
2588 sbData
->next
->prev
= sbData
->prev
;
2590 /* Modify the source ban's associated channel. */
2591 sbData
->channel
= target
;
2593 /* Insert the ban into the target channel's linked list. */
2594 sbData
->prev
= NULL
;
2595 sbData
->next
= target
->bans
;
2598 target
->bans
->prev
= sbData
;
2599 target
->bans
= sbData
;
2601 /* Update the user counts for the target channel. */
2606 /* Possible to assert (source->bans == NULL) here. */
2607 source
->bans
= NULL
;
2611 merge_data(struct chanData
*source
, struct chanData
*target
)
2613 /* Use more recent visited and owner-transfer time; use older
2614 * registered time. Bitwise or may_opchan. Use higher max.
2615 * Do not touch last_refresh, ban count or user counts.
2617 if(source
->visited
> target
->visited
)
2618 target
->visited
= source
->visited
;
2619 if(source
->registered
< target
->registered
)
2620 target
->registered
= source
->registered
;
2621 if(source
->ownerTransfer
> target
->ownerTransfer
)
2622 target
->ownerTransfer
= source
->ownerTransfer
;
2623 if(source
->may_opchan
)
2624 target
->may_opchan
= 1;
2625 if(source
->max
> target
->max
)
2626 target
->max
= source
->max
;
2630 merge_channel(struct chanData
*source
, struct chanData
*target
)
2632 merge_users(source
, target
);
2633 merge_bans(source
, target
);
2634 merge_data(source
, target
);
2637 static CHANSERV_FUNC(cmd_merge
)
2639 struct userData
*target_user
;
2640 struct chanNode
*target
;
2641 char reason
[MAXLEN
];
2645 /* Make sure the target channel exists and is registered to the user
2646 performing the command. */
2647 if(!(target
= GetChannel(argv
[1])))
2649 reply("MSG_INVALID_CHANNEL");
2653 if(!target
->channel_info
)
2655 reply("CSMSG_NOT_REGISTERED", target
->name
);
2659 if(IsProtected(channel
->channel_info
))
2661 reply("CSMSG_MERGE_NODELETE");
2665 if(IsSuspended(target
->channel_info
))
2667 reply("CSMSG_MERGE_SUSPENDED");
2671 if(channel
== target
)
2673 reply("CSMSG_MERGE_SELF");
2677 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2678 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2680 reply("CSMSG_MERGE_NOT_OWNER");
2684 /* Merge the channel structures and associated data. */
2685 merge_channel(channel
->channel_info
, target
->channel_info
);
2686 spamserv_cs_move_merge(user
, channel
, target
, 0);
2687 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2688 unregister_channel(channel
->channel_info
, reason
);
2689 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2693 static CHANSERV_FUNC(cmd_opchan
)
2695 struct mod_chanmode change
;
2696 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2698 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2701 channel
->channel_info
->may_opchan
= 0;
2702 mod_chanmode_init(&change
);
2704 change
.args
[0].mode
= MODE_CHANOP
;
2705 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2706 mod_chanmode_announce(chanserv
, channel
, &change
);
2707 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2711 static CHANSERV_FUNC(cmd_adduser
)
2713 struct userData
*actee
;
2714 struct userData
*actor
;
2715 struct handle_info
*handle
;
2716 unsigned short access
;
2720 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2722 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2726 access
= user_level_from_name(argv
[2], UL_OWNER
);
2729 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2733 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2734 if(actor
->access
<= access
)
2736 reply("CSMSG_NO_BUMP_ACCESS");
2740 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2742 // 'kevin must first authenticate with AuthServ.' is sent to user
2743 struct userNode
*unode
;
2744 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2747 if(find_adduser_pending(channel
, unode
)) {
2748 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2751 if(IsInChannel(channel
, unode
)) {
2752 reply("CSMSG_ADDUSER_PENDING");
2753 add_adduser_pending(channel
, unode
, access
);
2754 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2756 /* this results in user must auth AND not in chan errors. too confusing..
2758 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2766 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2768 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2772 time_t accessexpiry
= 0;
2773 unsigned int duration
= 0;
2775 if ((duration
= ParseInterval(argv
[3])))
2776 accessexpiry
= now
+ duration
;
2779 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
, accessexpiry
);
2780 scan_user_presence(actee
, NULL
);
2783 timeq_add(accessexpiry
, chanserv_expire_tempuser
, actee
);
2785 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2789 static CHANSERV_FUNC(cmd_clvl
)
2791 struct handle_info
*handle
;
2792 struct userData
*victim
;
2793 struct userData
*actor
;
2794 unsigned short new_access
;
2795 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2799 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2801 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2804 if(handle
== user
->handle_info
&& !privileged
)
2806 reply("CSMSG_NO_SELF_CLVL");
2810 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2812 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2816 if(actor
->access
<= victim
->access
&& !privileged
)
2818 reply("MSG_USER_OUTRANKED", handle
->handle
);
2822 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2826 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2830 if(new_access
>= actor
->access
&& !privileged
)
2832 reply("CSMSG_NO_BUMP_ACCESS");
2836 time_t clvlexpiry
= 0;
2837 unsigned int duration
= 0;
2839 if ((duration
= ParseInterval(argv
[3])))
2840 clvlexpiry
= now
+ duration
;
2844 if (victim
->accessexpiry
> 0) {
2845 reply("CSMSG_NO_BUMP_EXPIRY");
2849 victim
->clvlexpiry
= clvlexpiry
;
2850 victim
->lastaccess
= victim
->access
;
2851 timeq_add(clvlexpiry
, chanserv_expire_tempclvl
, victim
);
2854 victim
->access
= new_access
;
2855 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2859 static CHANSERV_FUNC(cmd_deluser
)
2861 struct handle_info
*handle
;
2862 struct userData
*victim
;
2863 struct userData
*actor
;
2864 unsigned short access
;
2869 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2871 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2874 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2876 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2882 access
= user_level_from_name(argv
[1], UL_OWNER
);
2883 char *useraccess
= user_level_name_from_level(victim
->access
);
2886 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2889 if(strcasecmp(argv
[1], useraccess
))
2891 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2897 access
= victim
->access
;
2900 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2902 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2906 chan_name
= strdup(channel
->name
);
2907 del_channel_user(victim
, 1);
2908 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2914 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2916 struct userData
*actor
, *uData
, *next
;
2918 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2920 if(min_access
> max_access
)
2922 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2926 if((actor
->access
<= max_access
) && !IsHelping(user
))
2928 reply("CSMSG_NO_ACCESS");
2932 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2936 if((uData
->access
>= min_access
)
2937 && (uData
->access
<= max_access
)
2938 && match_ircglob(uData
->handle
->handle
, mask
))
2939 del_channel_user(uData
, 1);
2942 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2946 static CHANSERV_FUNC(cmd_mdelowner
)
2948 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2951 static CHANSERV_FUNC(cmd_mdelcoowner
)
2953 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
2956 static CHANSERV_FUNC(cmd_mdelmanager
)
2958 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
2961 static CHANSERV_FUNC(cmd_mdelop
)
2963 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
2966 static CHANSERV_FUNC(cmd_mdelhalfop
)
2968 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
2971 static CHANSERV_FUNC(cmd_mdelpeon
)
2973 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
2977 static CHANSERV_FUNC(cmd_levels
)
2979 struct helpfile_table tbl
;
2982 tbl
.length
= 6 + 1; // 6 levels
2985 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
2986 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
2987 tbl
.contents
[0][0] = "Level";
2988 tbl
.contents
[0][1] = "From";
2989 tbl
.contents
[0][2] = "-";
2990 tbl
.contents
[0][3] = "To";
2992 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2993 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
2994 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
2995 tbl
.contents
[ii
][2] = msnprintf(2, " ");
2996 tbl
.contents
[ii
][3] = msnprintf(1, "");
2998 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2999 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
3000 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
3001 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3002 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
3004 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3005 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
3006 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
3007 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3008 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
3010 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3011 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
3012 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
3013 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3014 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
3016 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3017 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
3018 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
3019 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3020 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
3022 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
3023 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
3024 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
3025 tbl
.contents
[ii
][2] = msnprintf(2, "-");
3026 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
3028 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3032 reply("CSMSG_LEVELS_HEADER");
3033 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3034 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3035 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3036 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3037 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3038 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3045 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
3047 struct banData
*bData
, *next
;
3048 char interval
[INTERVALLEN
];
3053 limit
= now
- duration
;
3054 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3058 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
3061 del_channel_ban(bData
);
3065 intervalString(interval
, duration
, user
->handle_info
);
3066 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
3071 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
)
3073 struct userData
*actor
, *uData
, *next
;
3074 char interval
[INTERVALLEN
];
3078 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3079 if(min_access
> max_access
)
3081 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
3085 if((actor
->access
<= max_access
) && !IsHelping(user
))
3087 reply("CSMSG_NO_ACCESS");
3092 limit
= now
- duration
;
3093 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
3097 if((uData
->seen
> limit
)
3099 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
3102 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
3103 || (!max_access
&& (uData
->access
< actor
->access
)))
3105 del_channel_user(uData
, 1);
3113 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
3115 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3119 static CHANSERV_FUNC(cmd_trim
)
3121 unsigned long duration
;
3122 unsigned short min_level
, max_level
;
3127 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
3128 duration
= ParseInterval(argv
[2]);
3131 reply("CSMSG_CANNOT_TRIM");
3135 if(!irccasecmp(argv
[1], "lamers"))
3137 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
3140 else if(!irccasecmp(argv
[1], "users"))
3142 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
3145 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
3147 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3150 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3152 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3157 reply("CSMSG_INVALID_TRIM", argv
[1]);
3162 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3163 to the user. cmd_all takes advantage of this. */
3164 static CHANSERV_FUNC(cmd_up
)
3166 struct mod_chanmode change
;
3167 struct userData
*uData
;
3170 mod_chanmode_init(&change
);
3172 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3173 if(!change
.args
[0].u
.member
)
3176 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3180 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3184 reply("CSMSG_GODMODE_UP", argv
[0]);
3187 else if(uData
->access
>= UL_OP
)
3189 change
.args
[0].mode
= MODE_CHANOP
;
3190 errmsg
= "CSMSG_ALREADY_OPPED";
3192 else if(uData
->access
>= UL_HALFOP
)
3194 change
.args
[0].mode
= MODE_HALFOP
;
3195 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3197 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3199 change
.args
[0].mode
= MODE_VOICE
;
3200 errmsg
= "CSMSG_ALREADY_VOICED";
3205 reply("CSMSG_NO_ACCESS");
3208 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3209 if(!change
.args
[0].mode
)
3212 reply(errmsg
, channel
->name
);
3215 modcmd_chanmode_announce(&change
);
3219 static CHANSERV_FUNC(cmd_down
)
3221 struct mod_chanmode change
;
3223 mod_chanmode_init(&change
);
3225 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3226 if(!change
.args
[0].u
.member
)
3229 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3233 if(!change
.args
[0].u
.member
->modes
)
3236 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3240 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3241 modcmd_chanmode_announce(&change
);
3245 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
)
3247 struct userData
*cList
;
3249 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3251 if(IsSuspended(cList
->channel
)
3252 || IsUserSuspended(cList
)
3253 || !GetUserMode(cList
->channel
->channel
, user
))
3256 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3262 static CHANSERV_FUNC(cmd_upall
)
3264 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3267 static CHANSERV_FUNC(cmd_downall
)
3269 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3272 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3273 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3276 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
)
3278 unsigned int ii
, valid
;
3279 struct userNode
*victim
;
3280 struct mod_chanmode
*change
;
3282 change
= mod_chanmode_alloc(argc
- 1);
3284 for(ii
=valid
=0; ++ii
< argc
; )
3286 if(!(victim
= GetUserH(argv
[ii
])))
3288 change
->args
[valid
].mode
= mode
;
3289 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3290 if(!change
->args
[valid
].u
.member
)
3292 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3297 change
->argc
= valid
;
3298 if(valid
< (argc
-1))
3299 reply("CSMSG_PROCESS_FAILED");
3302 modcmd_chanmode_announce(change
);
3303 reply(action
, channel
->name
);
3305 mod_chanmode_free(change
);
3309 static CHANSERV_FUNC(cmd_op
)
3311 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3314 static CHANSERV_FUNC(cmd_hop
)
3316 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3319 static CHANSERV_FUNC(cmd_deop
)
3321 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3324 static CHANSERV_FUNC(cmd_dehop
)
3326 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3329 static CHANSERV_FUNC(cmd_voice
)
3331 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3334 static CHANSERV_FUNC(cmd_devoice
)
3336 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3340 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3346 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3348 struct modeNode
*mn
= channel
->members
.list
[ii
];
3350 if(IsService(mn
->user
))
3353 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3356 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3360 victims
[(*victimCount
)++] = mn
;
3366 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3368 struct userNode
*victim
;
3369 struct modeNode
**victims
;
3370 unsigned int offset
, n
, victimCount
, duration
= 0;
3371 char *reason
= "Bye.", *ban
, *name
;
3372 char interval
[INTERVALLEN
];
3374 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3375 REQUIRE_PARAMS(offset
);
3378 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3379 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3381 /* Truncate the reason to a length of TOPICLEN, as
3382 the ircd does; however, leave room for an ellipsis
3383 and the kicker's nick. */
3384 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3388 if((victim
= GetUserH(argv
[1])))
3390 victims
= alloca(sizeof(victims
[0]));
3391 victims
[0] = GetUserMode(channel
, victim
);
3392 /* XXX: The comparison with ACTION_KICK is just because all
3393 * other actions can work on users outside the channel, and we
3394 * want to allow those (e.g. unbans) in that case. If we add
3395 * some other ejection action for in-channel users, change
3397 victimCount
= victims
[0] ? 1 : 0;
3399 if(IsService(victim
))
3402 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3406 if((action
== ACTION_KICK
) && !victimCount
)
3409 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3413 if(protect_user(victim
, user
, channel
->channel_info
, false))
3415 // This translates to send_message(user, cmd->parent->bot, ...)
3416 // if user is x3 (ctcp action) cmd is null and segfault.
3418 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3422 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3423 name
= victim
->nick
;
3427 if(!is_ircmask(argv
[1]))
3430 reply("MSG_NICK_UNKNOWN", argv
[1]);
3434 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3436 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3439 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3442 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3443 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3445 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3446 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3447 some creativity, but its not x3's job to be the ban censor anyway. */
3448 if(is_overmask(argv
[1]))
3451 reply("CSMSG_LAME_MASK", argv
[1]);
3455 if((action
== ACTION_KICK
) && (victimCount
== 0))
3458 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3462 name
= ban
= strdup(argv
[1]);
3465 /* Truncate the ban in place if necessary; we must ensure
3466 that 'ban' is a valid ban mask before sanitizing it. */
3467 sanitize_ircmask(ban
);
3469 if(action
& ACTION_ADD_LAMER
)
3471 struct banData
*bData
, *next
;
3473 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3476 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3481 if(action
& ACTION_ADD_TIMED_LAMER
)
3483 duration
= ParseInterval(argv
[2]);
3488 reply("CSMSG_DURATION_TOO_LOW");
3492 else if(duration
> (86400 * 365 * 2))
3495 reply("CSMSG_DURATION_TOO_HIGH");
3502 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3504 if(match_ircglobs(bData
->mask
, ban
))
3506 int exact
= !irccasecmp(bData
->mask
, ban
);
3508 /* The ban is redundant; there is already a ban
3509 with the same effect in place. */
3513 free(bData
->reason
);
3514 bData
->reason
= strdup(reason
);
3515 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3517 reply("CSMSG_REASON_CHANGE", ban
);
3521 if(exact
&& bData
->expires
)
3525 /* If the ban matches an existing one exactly,
3526 extend the expiration time if the provided
3527 duration is longer. */
3528 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3530 bData
->expires
= now
+ duration
;
3541 /* Delete the expiration timeq entry and
3542 requeue if necessary. */
3543 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3546 timeq_add(bData
->expires
, expire_ban
, bData
);
3550 /* automated kickban, dont reply */
3553 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3555 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3561 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3568 if(match_ircglobs(ban
, bData
->mask
))
3570 /* The ban we are adding makes previously existing
3571 bans redundant; silently remove them. */
3572 del_channel_ban(bData
);
3576 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
);
3578 name
= ban
= strdup(bData
->mask
);
3582 /* WHAT DOES THIS DO?? -Rubin */
3583 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3585 extern const char *hidden_host_suffix
;
3586 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3588 unsigned int l1
, l2
;
3591 l2
= strlen(old_name
);
3594 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3596 new_mask
= malloc(MAXLEN
);
3597 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3599 name
= ban
= new_mask
;
3604 if(action
& ACTION_BAN
)
3606 unsigned int exists
;
3607 struct mod_chanmode
*change
;
3609 if(channel
->banlist
.used
>= MAXBANS
)
3612 reply("CSMSG_BANLIST_FULL", channel
->name
);
3617 exists
= ChannelBanExists(channel
, ban
);
3618 change
= mod_chanmode_alloc(victimCount
+ 1);
3619 for(n
= 0; n
< victimCount
; ++n
)
3621 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3622 change
->args
[n
].u
.member
= victims
[n
];
3626 change
->args
[n
].mode
= MODE_BAN
;
3627 change
->args
[n
++].u
.hostmask
= ban
;
3631 modcmd_chanmode_announce(change
);
3633 mod_chanmode_announce(chanserv
, channel
, change
);
3634 mod_chanmode_free(change
);
3636 if(exists
&& (action
== ACTION_BAN
))
3639 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3645 if(action
& ACTION_ADD_LAMER
)
3647 char kick_reason
[MAXLEN
];
3648 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3650 for(n
= 0; n
< victimCount
; n
++) {
3651 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3652 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3656 else if(action
& ACTION_KICK
)
3658 char kick_reason
[MAXLEN
];
3659 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3661 for(n
= 0; n
< victimCount
; n
++) {
3662 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3668 /* No response, since it was automated. */
3670 else if(action
& ACTION_ADD_LAMER
)
3673 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3675 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3677 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3678 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3679 else if(action
& ACTION_BAN
)
3680 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3681 else if(action
& ACTION_KICK
&& victimCount
)
3682 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3688 static CHANSERV_FUNC(cmd_kickban
)
3690 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3693 static CHANSERV_FUNC(cmd_kick
)
3695 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3698 static CHANSERV_FUNC(cmd_ban
)
3700 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3703 static CHANSERV_FUNC(cmd_addlamer
)
3705 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3708 static CHANSERV_FUNC(cmd_addtimedlamer
)
3710 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3713 static struct mod_chanmode
*
3714 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3716 struct mod_chanmode
*change
;
3717 unsigned char *match
;
3718 unsigned int ii
, count
;
3720 match
= alloca(bans
->used
);
3723 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3725 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3726 MATCH_USENICK
| MATCH_VISIBLE
);
3733 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3735 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3742 change
= mod_chanmode_alloc(count
);
3743 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3747 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3748 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3750 assert(count
== change
->argc
);
3754 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3756 unsigned int jj
, ii
, count
;
3758 struct chanData
*channel
;
3760 struct mod_chanmode
*change
;
3762 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3763 /* Walk through every channel */
3764 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3765 switch(channel
->chOpts
[chBanTimeout
])
3767 default: case '0': continue; /* Dont remove bans in this chan */
3768 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3769 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3770 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3771 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3772 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3775 /* First find out how many bans were going to unset */
3776 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3777 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3781 /* At least one ban, so setup a removal */
3782 change
= mod_chanmode_alloc(count
);
3784 /* Walk over every ban in this channel.. */
3785 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3786 bn
= channel
->channel
->banlist
.list
[jj
];
3787 if (bn
->set
< bantimeout
) {
3788 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3790 /* Add this ban to the mode change */
3791 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3792 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3794 /* Pull this ban out of the list */
3795 banList_remove(&(channel
->channel
->banlist
), bn
);
3800 /* Send the modes to IRC */
3801 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3803 /* free memory from strdup above */
3804 for(ii
= 0; ii
< count
; ++ii
)
3805 free((char*)change
->args
[ii
].u
.hostmask
);
3807 mod_chanmode_free(change
);
3810 /* Set this function to run again */
3811 if(chanserv_conf
.ban_timeout_frequency
)
3812 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3817 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3819 struct userNode
*actee
;
3825 /* may want to allow a comma delimited list of users... */
3826 if(!(actee
= GetUserH(argv
[1])))
3828 if(!is_ircmask(argv
[1]))
3830 reply("MSG_NICK_UNKNOWN", argv
[1]);
3834 mask
= strdup(argv
[1]);
3837 /* We don't sanitize the mask here because ircu
3839 if(action
& ACTION_UNBAN
)
3841 struct mod_chanmode
*change
;
3842 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3847 modcmd_chanmode_announce(change
);
3848 for(ii
= 0; ii
< change
->argc
; ++ii
)
3849 free((char*)change
->args
[ii
].u
.hostmask
);
3850 mod_chanmode_free(change
);
3855 if(action
& ACTION_DEL_LAMER
)
3857 struct banData
*ban
, *next
;
3859 ban
= channel
->channel_info
->bans
; /* lamers */
3863 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3866 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3871 del_channel_ban(ban
);
3878 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3880 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3886 static CHANSERV_FUNC(cmd_unban
)
3888 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3891 static CHANSERV_FUNC(cmd_dellamer
)
3893 /* it doesn't necessarily have to remove the channel ban - may want
3894 to make that an option. */
3895 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3898 static CHANSERV_FUNC(cmd_unbanme
)
3900 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3901 long flags
= ACTION_UNBAN
;
3903 /* remove permanent bans if the user has the proper access. */
3904 if(uData
->access
>= UL_MANAGER
)
3905 flags
|= ACTION_DEL_LAMER
;
3907 argv
[1] = user
->nick
;
3908 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3911 static CHANSERV_FUNC(cmd_unbanall
)
3913 struct mod_chanmode
*change
;
3916 if(!channel
->banlist
.used
)
3918 reply("CSMSG_NO_BANS", channel
->name
);
3922 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3923 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3925 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3926 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3928 modcmd_chanmode_announce(change
);
3929 for(ii
= 0; ii
< change
->argc
; ++ii
)
3930 free((char*)change
->args
[ii
].u
.hostmask
);
3931 mod_chanmode_free(change
);
3932 reply("CSMSG_BANS_REMOVED", channel
->name
);
3936 static CHANSERV_FUNC(cmd_open
)
3938 struct mod_chanmode
*change
;
3941 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3943 change
= mod_chanmode_alloc(0);
3944 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3945 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3946 && channel
->channel_info
->modes
.modes_set
)
3947 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3948 modcmd_chanmode_announce(change
);
3949 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3950 for(ii
= 0; ii
< change
->argc
; ++ii
)
3951 free((char*)change
->args
[ii
].u
.hostmask
);
3952 mod_chanmode_free(change
);
3956 static CHANSERV_FUNC(cmd_myaccess
)
3958 static struct string_buffer sbuf
;
3959 struct handle_info
*target_handle
;
3960 struct userData
*uData
;
3963 target_handle
= user
->handle_info
;
3964 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3966 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
3968 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3971 if(!target_handle
->channels
)
3973 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3977 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3978 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3980 struct chanData
*cData
= uData
->channel
;
3982 if(uData
->access
> UL_OWNER
)
3984 if(IsProtected(cData
)
3985 && (target_handle
!= user
->handle_info
)
3986 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3989 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3990 if(uData
->flags
== USER_AUTO_OP
)
3991 string_buffer_append(&sbuf
, ',');
3992 if(IsUserSuspended(uData
))
3993 string_buffer_append(&sbuf
, 's');
3994 if(IsUserAutoOp(uData
))
3996 if(uData
->access
>= UL_OP
)
3997 string_buffer_append(&sbuf
, 'o');
3998 else if(uData
->access
>= UL_HALFOP
)
3999 string_buffer_append(&sbuf
, 'h');
4000 else if(uData
->access
>= UL_PEON
)
4001 string_buffer_append(&sbuf
, 'v');
4003 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4004 string_buffer_append(&sbuf
, 'i');
4005 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
4006 string_buffer_append(&sbuf
, 'j');
4008 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
4010 string_buffer_append_string(&sbuf
, ")]");
4011 string_buffer_append(&sbuf
, '\0');
4012 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
4018 static CHANSERV_FUNC(cmd_access
)
4020 struct userNode
*target
;
4021 struct handle_info
*target_handle
;
4022 struct userData
*uData
;
4024 char prefix
[MAXLEN
];
4029 target_handle
= target
->handle_info
;
4031 else if((target
= GetUserH(argv
[1])))
4033 target_handle
= target
->handle_info
;
4035 else if(argv
[1][0] == '*')
4037 if(!(target_handle
= get_handle_info(argv
[1]+1)))
4039 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
4045 reply("MSG_NICK_UNKNOWN", argv
[1]);
4049 assert(target
|| target_handle
);
4051 if(target
== chanserv
)
4053 reply("CSMSG_IS_CHANSERV");
4061 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
4066 reply("MSG_USER_AUTHENTICATE", target
->nick
);
4069 reply("MSG_AUTHENTICATE");
4075 const char *epithet
= NULL
, *type
= NULL
;
4078 epithet
= chanserv_conf
.irc_operator_epithet
;
4081 else if(IsNetworkHelper(target
))
4083 epithet
= chanserv_conf
.network_helper_epithet
;
4084 type
= "network helper";
4086 else if(IsSupportHelper(target
))
4088 epithet
= chanserv_conf
.support_helper_epithet
;
4089 type
= "support helper";
4093 if(target_handle
->epithet
)
4094 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
4096 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
4098 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
4102 sprintf(prefix
, "%s", target_handle
->handle
);
4105 if(!channel
->channel_info
)
4107 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4111 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
4112 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
4113 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
4115 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
4116 /* To prevent possible information leaks, only show infolines
4117 * if the requestor is in the channel or it's their own
4119 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
4121 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
4123 /* Likewise, only say it's suspended if the user has active
4124 * access in that channel or it's their own entry. */
4125 if(IsUserSuspended(uData
)
4126 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
4127 || (user
->handle_info
== uData
->handle
)))
4129 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4134 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4140 /* This is never used...
4142 zoot_list(struct listData *list)
4144 struct userData *uData;
4145 unsigned int start, curr, highest, lowest;
4146 struct helpfile_table tmp_table;
4147 const char **temp, *msg;
4149 if(list->table.length == 1)
4152 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);
4154 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));
4155 msg = user_find_message(list->user, "MSG_NONE");
4156 send_message_type(4, list->user, list->bot, " %s", msg);
4158 tmp_table.width = list->table.width;
4159 tmp_table.flags = list->table.flags;
4160 list->table.contents[0][0] = " ";
4161 highest = list->highest;
4162 if(list->lowest != 0)
4163 lowest = list->lowest;
4164 else if(highest < 100)
4167 lowest = highest - 100;
4168 for(start = curr = 1; curr < list->table.length; )
4170 uData = list->users[curr-1];
4171 list->table.contents[curr++][0] = " ";
4172 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4175 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);
4177 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));
4178 temp = list->table.contents[--start];
4179 list->table.contents[start] = list->table.contents[0];
4180 tmp_table.contents = list->table.contents + start;
4181 tmp_table.length = curr - start;
4182 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4183 list->table.contents[start] = temp;
4185 highest = lowest - 1;
4186 lowest = (highest < 100) ? 0 : (highest - 99);
4193 normal_list(struct listData
*list
)
4197 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
);
4199 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
));
4200 if(list
->table
.length
== 1)
4202 msg
= user_find_message(list
->user
, "MSG_NONE");
4203 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4206 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4209 /* if these need changed, uncomment and customize
4211 clean_list(struct listData *list)
4215 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);
4217 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));
4218 if(list->table.length == 1)
4220 msg = user_find_message(list->user, "MSG_NONE");
4221 send_message_type(4, list->user, list->bot, " %s", msg);
4224 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4228 advanced_list(struct listData *list)
4232 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);
4234 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));
4235 if(list->table.length == 1)
4237 msg = user_find_message(list->user, "MSG_NONE");
4238 send_message_type(4, list->user, list->bot, " %s", msg);
4241 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4245 classic_list(struct listData *list)
4249 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4251 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4252 if(list->table.length == 1)
4254 msg = user_find_message(list->user, "MSG_NONE");
4255 send_message_type(4, list->user, list->bot, " %s", msg);
4258 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4263 userData_access_comp(const void *arg_a
, const void *arg_b
)
4265 const struct userData
*a
= *(struct userData
**)arg_a
;
4266 const struct userData
*b
= *(struct userData
**)arg_b
;
4268 if(a
->access
!= b
->access
)
4269 res
= b
->access
- a
->access
;
4271 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4276 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4278 void (*send_list
)(struct listData
*);
4279 struct userData
*uData
;
4280 struct listData lData
;
4281 unsigned int matches
;
4287 lData
.bot
= cmd
->parent
->bot
;
4288 lData
.channel
= channel
;
4289 lData
.lowest
= lowest
;
4290 lData
.highest
= highest
;
4291 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4292 send_list
= normal_list
;
4293 /* What does the following line do exactly?? */
4294 /*(void)zoot_list; ** since it doesn't show user levels */
4297 if(user->handle_info)
4299 switch(user->handle_info->userlist_style)
4301 case HI_STYLE_CLEAN:
4302 send_list = clean_list;
4304 case HI_STYLE_ADVANCED:
4305 send_list = advanced_list;
4307 case HI_STYLE_CLASSIC:
4308 send_list = classic_list;
4310 case HI_STYLE_NORMAL:
4312 send_list = normal_list;
4317 send_list
= normal_list
;
4319 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4321 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4323 if((uData
->access
< lowest
)
4324 || (uData
->access
> highest
)
4325 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4327 lData
.users
[matches
++] = uData
;
4329 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4331 lData
.table
.length
= matches
+1;
4332 lData
.table
.flags
= TABLE_NO_FREE
;
4333 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4335 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4336 lData
.table
.width
= 6; /* with level = 6 */
4338 lData
.table
.width
= 5; /* without = 5 */
4339 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4340 lData
.table
.contents
[0] = ary
;
4341 if(user
->handle_info
) {
4342 switch(user
->handle_info
->userlist_style
) {
4343 case HI_STYLE_CLASSIC
:
4346 case HI_STYLE_ADVANCED
:
4347 ary
[i
++] = "Access";
4350 case HI_STYLE_CLEAN
:
4351 ary
[i
++] = "Access";
4353 case HI_STYLE_NORMAL
:
4355 ary
[i
++] = "Access";
4360 ary
[i
++] = "Access";
4362 ary
[i
++] = "Account";
4363 ary
[i
] = "Last Seen";
4365 ary
[i
++] = "Status";
4366 ary
[i
++] = "Expiry";
4367 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4369 struct userData
*uData
= lData
.users
[matches
-1];
4370 char seen
[INTERVALLEN
];
4373 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4374 lData
.table
.contents
[matches
] = ary
;
4375 if(user
->handle_info
) {
4376 switch(user
->handle_info
->userlist_style
) {
4377 case HI_STYLE_CLASSIC
:
4378 ary
[i
++] = strtab(uData
->access
);
4380 case HI_STYLE_ADVANCED
:
4381 ary
[i
++] = user_level_name_from_level(uData
->access
);
4382 ary
[i
++] = strtab(uData
->access
);
4384 case HI_STYLE_CLEAN
:
4385 ary
[i
++] = user_level_name_from_level(uData
->access
);
4387 case HI_STYLE_NORMAL
:
4389 ary
[i
++] = user_level_name_from_level(uData
->access
);
4394 ary
[i
++] = user_level_name_from_level(uData
->access
);
4396 ary
[i
++] = uData
->handle
->handle
;
4399 else if(!uData
->seen
)
4402 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4403 ary
[i
] = strdup(ary
[i
]);
4405 if(IsUserSuspended(uData
))
4406 ary
[i
++] = "Suspended";
4407 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4408 ary
[i
++] = "Vacation";
4410 ary
[i
++] = "Normal";
4412 if ((uData
->accessexpiry
> 0) || (uData
->clvlexpiry
> 0)) {
4413 char delay
[INTERVALLEN
];
4416 if (uData
->accessexpiry
> 0) {
4417 diff
= uData
->accessexpiry
- now
;
4418 intervalString(delay
, diff
, user
->handle_info
);
4420 diff
= uData
->clvlexpiry
- now
;
4421 intervalString(delay
, diff
, user
->handle_info
);
4429 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4431 /* Free strdup above */
4432 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4433 free(lData
.table
.contents
[matches
]);
4435 free(lData
.table
.contents
[0]);
4436 free(lData
.table
.contents
);
4440 /* Remove this now that debugging is over? or improve it for
4441 * users? Would it be better tied into USERS somehow? -Rubin */
4442 static CHANSERV_FUNC(cmd_pending
)
4444 struct adduserPending
*ap
;
4445 reply("CSMSG_ADDUSER_PENDING_HEADER");
4446 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4448 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4449 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4450 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4454 static CHANSERV_FUNC(cmd_users
)
4456 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4459 static CHANSERV_FUNC(cmd_wlist
)
4461 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4464 static CHANSERV_FUNC(cmd_clist
)
4466 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4469 static CHANSERV_FUNC(cmd_mlist
)
4471 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4474 static CHANSERV_FUNC(cmd_olist
)
4476 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4479 static CHANSERV_FUNC(cmd_hlist
)
4481 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4484 static CHANSERV_FUNC(cmd_plist
)
4486 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4489 static CHANSERV_FUNC(cmd_lamers
)
4491 struct helpfile_table tbl
;
4492 unsigned int matches
= 0, timed
= 0, ii
;
4493 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4494 const char *msg_never
, *triggered
, *expires
;
4495 struct banData
*ban
, **bans
; /* lamers */
4502 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4503 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4506 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4508 if(search
&& !match_ircglobs(search
, ban
->mask
))
4510 bans
[matches
++] = ban
;
4515 tbl
.length
= matches
+ 1;
4516 tbl
.width
= 4 + timed
;
4518 tbl
.flags
= TABLE_NO_FREE
;
4519 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4520 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4521 tbl
.contents
[0][0] = "Mask";
4522 tbl
.contents
[0][1] = "Set By";
4523 tbl
.contents
[0][2] = "Triggered";
4526 tbl
.contents
[0][3] = "Expires";
4527 tbl
.contents
[0][4] = "Reason";
4530 tbl
.contents
[0][3] = "Reason";
4533 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4534 /* reply("MSG_NONE"); */
4535 free(tbl
.contents
[0]);
4540 msg_never
= user_find_message(user
, "MSG_NEVER");
4541 for(ii
= 0; ii
< matches
; )
4547 else if(ban
->expires
)
4548 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4550 expires
= msg_never
;
4553 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4555 triggered
= msg_never
;
4557 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4558 tbl
.contents
[ii
][0] = ban
->mask
;
4559 tbl
.contents
[ii
][1] = ban
->owner
;
4560 tbl
.contents
[ii
][2] = strdup(triggered
);
4563 tbl
.contents
[ii
][3] = strdup(expires
);
4564 tbl
.contents
[ii
][4] = ban
->reason
;
4567 tbl
.contents
[ii
][3] = ban
->reason
;
4569 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4570 /* reply("MSG_MATCH_COUNT", matches); */
4571 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4573 free((char*)tbl
.contents
[ii
][2]);
4575 free((char*)tbl
.contents
[ii
][3]);
4576 free(tbl
.contents
[ii
]);
4578 free(tbl
.contents
[0]);
4585 * return + if the user does NOT have the right to set the topic, and
4586 * the topic is changed.
4589 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4591 struct chanData
*cData
= channel
->channel_info
;
4592 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4594 else if(cData
->topic
)
4595 return irccasecmp(new_topic
, cData
->topic
);
4602 * Makes a givin topic fit into a givin topic mask and returns
4605 * topic_mask - the mask to conform to
4606 * topic - the topic to make conform
4607 * new_topic - the pre-allocated char* to put the new topic into
4609 * modifies: new_topic
4612 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4614 //char *topic_mask = cData->topic_mask;
4616 int pos
=0, starpos
=-1, dpos
=0, len
;
4618 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4625 strcpy(new_topic
, "");
4628 len
= strlen(topic
);
4629 if((dpos
+ len
) > TOPICLEN
)
4630 len
= TOPICLEN
+ 1 - dpos
;
4631 memcpy(new_topic
+dpos
, topic
, len
);
4635 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4636 default: new_topic
[dpos
++] = tchar
; break;
4639 if((dpos
> TOPICLEN
) || tchar
)
4641 strcpy(new_topic
, "");
4644 new_topic
[dpos
] = 0;
4648 static CHANSERV_FUNC(cmd_topic
)
4650 struct chanData
*cData
;
4654 #ifdef WITH_PROTOCOL_P10
4658 cData
= channel
->channel_info
;
4663 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4664 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4665 reply("CSMSG_TOPIC_SET", cData
->topic
);
4669 reply("CSMSG_NO_TOPIC", channel
->name
);
4673 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4674 /* If they say "!topic *", use an empty topic. */
4675 if((topic
[0] == '*') && (topic
[1] == 0))
4678 if(bad_topic(channel
, user
, topic
))
4680 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4685 /* If there is a topicmask set, and the new topic doesnt match, make it */
4686 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4688 char *topic_mask
= cData
->topic_mask
;
4689 char new_topic
[TOPICLEN
+1];
4691 /* make a new topic fitting mask */
4692 conform_topic(topic_mask
, topic
, new_topic
);
4695 /* Topic couldnt fit into mask, was too long */
4696 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4697 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4700 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4702 else /* No mask set, just set the topic */
4703 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4706 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4708 /* Grab the topic and save it as the default topic. */
4710 cData
->topic
= strdup(channel
->topic
);
4716 static CHANSERV_FUNC(cmd_mode
)
4718 struct userData
*uData
;
4719 struct mod_chanmode
*change
;
4724 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4725 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4729 change
= &channel
->channel_info
->modes
;
4730 if(change
->modes_set
|| change
->modes_clear
) {
4731 modcmd_chanmode_announce(change
);
4732 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4734 reply("CSMSG_NO_MODES", channel
->name
);
4738 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4740 base_oplevel
= MAXOPLEVEL
;
4741 else if (uData
->access
>= UL_OWNER
)
4744 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4745 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4749 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4753 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4754 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4757 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4758 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4762 modcmd_chanmode_announce(change
);
4763 mod_chanmode_free(change
);
4764 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4768 static CHANSERV_FUNC(cmd_invite
)
4770 struct userData
*uData
;
4771 struct userNode
*invite
;
4773 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4777 if(!(invite
= GetUserH(argv
[1])))
4779 reply("MSG_NICK_UNKNOWN", argv
[1]);
4786 if(GetUserMode(channel
, invite
))
4788 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4796 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4797 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4800 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4803 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4805 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4806 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4807 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4813 irc_invite(chanserv
, invite
, channel
);
4815 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4820 static CHANSERV_FUNC(cmd_inviteme
)
4822 if(GetUserMode(channel
, user
))
4824 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4827 if(channel
->channel_info
4828 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4830 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4833 irc_invite(cmd
->parent
->bot
, user
, channel
);
4838 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4841 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4843 /* We display things based on two dimensions:
4844 * - Issue time: present or absent
4845 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4846 * (in order of precedence, so something both expired and revoked
4847 * only counts as revoked)
4849 combo
= (suspended
->issued
? 4 : 0)
4850 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4852 case 0: /* no issue time, indefinite expiration */
4853 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4855 case 1: /* no issue time, expires in future */
4856 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4857 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4859 case 2: /* no issue time, expired */
4860 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4861 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4863 case 3: /* no issue time, revoked */
4864 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4865 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4867 case 4: /* issue time set, indefinite expiration */
4868 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4869 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4871 case 5: /* issue time set, expires in future */
4872 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4873 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4874 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4876 case 6: /* issue time set, expired */
4877 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4878 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4879 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4881 case 7: /* issue time set, revoked */
4882 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4883 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4884 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4887 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4893 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4896 const char *fmt
= "%a %b %d %H:%M %Y";
4897 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4899 if(giveownership
->staff_issuer
)
4901 if(giveownership
->reason
)
4902 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4903 giveownership
->target
, giveownership
->target_access
,
4904 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4906 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4907 giveownership
->target
, giveownership
->target_access
,
4908 giveownership
->staff_issuer
, buf
);
4912 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4917 static CHANSERV_FUNC(cmd_info
)
4919 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4920 struct userData
*uData
, *owner
;
4921 struct chanData
*cData
;
4922 struct do_not_register
*dnr
;
4927 cData
= channel
->channel_info
;
4928 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4929 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4932 uData
= GetChannelUser(cData
, user
->handle_info
);
4933 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4935 mod_chanmode_format(&cData
->modes
, modes
);
4936 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4937 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4940 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4944 note
= iter_data(it
);
4945 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4948 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4949 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4952 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4953 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4954 if(owner
->access
== UL_OWNER
)
4955 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4956 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4957 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4958 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4960 privileged
= IsStaff(user
);
4961 /* if(privileged) */
4962 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4963 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4964 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4966 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4967 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4969 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4971 struct suspended
*suspended
;
4972 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4973 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4974 show_suspension_info(cmd
, user
, suspended
);
4976 else if(IsSuspended(cData
))
4978 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4979 show_suspension_info(cmd
, user
, cData
->suspended
);
4981 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4983 struct giveownership
*giveownership
;
4984 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4985 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4986 show_giveownership_info(cmd
, user
, giveownership
);
4988 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4989 reply("CSMSG_CHANNEL_END");
4991 reply("CSMSG_CHANNEL_END_CLEAN");
4995 static CHANSERV_FUNC(cmd_netinfo
)
4997 extern time_t boot_time
;
4998 extern unsigned long burst_length
;
4999 char interval
[INTERVALLEN
];
5001 reply("CSMSG_NETWORK_INFO");
5002 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
5003 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
5004 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
5005 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
5006 reply("CSMSG_NETWORK_LAMERS", banCount
);
5007 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
5008 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
5009 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
5014 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
5016 struct helpfile_table table
;
5018 struct userNode
*user
;
5023 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5024 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
5025 for(nn
=0; nn
<list
->used
; nn
++)
5027 user
= list
->list
[nn
];
5028 if(user
->modes
& skip_flags
)
5032 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
5035 nick
= alloca(strlen(user
->nick
)+3);
5036 sprintf(nick
, "(%s)", user
->nick
);
5040 table
.contents
[table
.length
][0] = nick
;
5043 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
5046 static CHANSERV_FUNC(cmd_ircops
)
5048 reply("CSMSG_STAFF_OPERS");
5049 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
5053 static CHANSERV_FUNC(cmd_helpers
)
5055 reply("CSMSG_STAFF_HELPERS");
5056 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
5060 static CHANSERV_FUNC(cmd_staff
)
5062 reply("CSMSG_NETWORK_STAFF");
5063 cmd_ircops(CSFUNC_ARGS
);
5064 cmd_helpers(CSFUNC_ARGS
);
5068 static CHANSERV_FUNC(cmd_peek
)
5070 struct modeNode
*mn
;
5071 char modes
[MODELEN
];
5073 struct helpfile_table table
;
5075 irc_make_chanmode(channel
, modes
);
5077 reply("CSMSG_PEEK_INFO", channel
->name
);
5078 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5080 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
5081 reply("CSMSG_PEEK_MODES", modes
);
5082 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
5086 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
5087 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
5088 for(n
= 0; n
< channel
->members
.used
; n
++)
5090 mn
= channel
->members
.list
[n
];
5091 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
5093 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
5094 table
.contents
[table
.length
][0] = mn
->user
->nick
;
5099 reply("CSMSG_PEEK_OPS");
5100 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
5103 reply("CSMSG_PEEK_NO_OPS");
5104 reply("CSMSG_PEEK_END");
5108 static MODCMD_FUNC(cmd_wipeinfo
)
5110 struct handle_info
*victim
;
5111 struct userData
*ud
, *actor
;
5114 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5115 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
5117 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
5119 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
5122 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
5124 reply("MSG_USER_OUTRANKED", victim
->handle
);
5130 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
5135 resync_channel(struct chanNode
*channel
)
5137 struct mod_chanmode
*changes
;
5138 struct chanData
*cData
= channel
->channel_info
;
5139 unsigned int ii
, used
;
5141 /* 6 = worst case -ovh+ovh on everyone */
5142 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
5143 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
5145 struct modeNode
*mn
= channel
->members
.list
[ii
];
5146 struct userData
*uData
;
5148 if(IsService(mn
->user
))
5152 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
5154 /* If the channel is in no-mode mode, de-mode EVERYONE */
5155 if(cData
->chOpts
[chAutomode
] == 'n')
5159 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5160 changes
->args
[used
++].u
.member
= mn
;
5163 else /* Give various userlevels their modes.. */
5165 if(uData
&& uData
->access
>= UL_OP
)
5167 if(!(mn
->modes
& MODE_CHANOP
))
5169 changes
->args
[used
].mode
= MODE_CHANOP
;
5170 changes
->args
[used
++].u
.member
= mn
;
5173 else if(uData
&& uData
->access
>= UL_HALFOP
)
5175 if(mn
->modes
& MODE_CHANOP
)
5177 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5178 changes
->args
[used
++].u
.member
= mn
;
5180 if(!(mn
->modes
& MODE_HALFOP
))
5182 changes
->args
[used
].mode
= MODE_HALFOP
;
5183 changes
->args
[used
++].u
.member
= mn
;
5186 else if(uData
&& uData
->access
>= UL_PEON
)
5188 if(mn
->modes
& MODE_CHANOP
)
5190 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5191 changes
->args
[used
++].u
.member
= mn
;
5193 if(mn
->modes
& MODE_HALFOP
)
5195 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5196 changes
->args
[used
++].u
.member
= mn
;
5198 /* Don't voice peons if were in mode m */
5199 if( cData
->chOpts
[chAutomode
] == 'm')
5201 if(mn
->modes
& MODE_VOICE
)
5203 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5204 changes
->args
[used
++].u
.member
= mn
;
5207 /* otherwise, make user they do have voice */
5208 else if(!(mn
->modes
& MODE_VOICE
))
5210 changes
->args
[used
].mode
= MODE_VOICE
;
5211 changes
->args
[used
++].u
.member
= mn
;
5214 else /* They arnt on the userlist.. */
5216 /* If we voice everyone, but they dont.. */
5217 if(cData
->chOpts
[chAutomode
] == 'v')
5219 /* Remove anything except v */
5220 if(mn
->modes
& ~MODE_VOICE
)
5222 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5223 changes
->args
[used
++].u
.member
= mn
;
5226 if(!(mn
->modes
& MODE_VOICE
))
5228 changes
->args
[used
].mode
= MODE_VOICE
;
5229 changes
->args
[used
++].u
.member
= mn
;
5232 /* If we hop everyone, but they dont.. */
5233 else if(cData
->chOpts
[chAutomode
] == 'h')
5235 /* Remove anything except h */
5236 if(mn
->modes
& ~MODE_HALFOP
)
5238 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5239 changes
->args
[used
++].u
.member
= mn
;
5242 if(!(mn
->modes
& MODE_HALFOP
))
5244 changes
->args
[used
].mode
= MODE_HALFOP
;
5245 changes
->args
[used
++].u
.member
= mn
;
5248 /* If we op everyone, but they dont.. */
5249 else if(cData
->chOpts
[chAutomode
] == 'o')
5251 /* Remove anything except h */
5252 if(mn
->modes
& ~MODE_CHANOP
)
5254 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5255 changes
->args
[used
++].u
.member
= mn
;
5258 if(!(mn
->modes
& MODE_CHANOP
))
5260 changes
->args
[used
].mode
= MODE_CHANOP
;
5261 changes
->args
[used
++].u
.member
= mn
;
5264 /* they have no excuse for having modes, de-everything them */
5269 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5270 changes
->args
[used
++].u
.member
= mn
;
5276 changes
->argc
= used
;
5277 mod_chanmode_announce(chanserv
, channel
, changes
);
5278 mod_chanmode_free(changes
);
5281 static CHANSERV_FUNC(cmd_resync
)
5283 resync_channel(channel
);
5284 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5288 static CHANSERV_FUNC(cmd_seen
)
5290 struct userData
*uData
;
5291 struct handle_info
*handle
;
5292 char seen
[INTERVALLEN
];
5296 if(!irccasecmp(argv
[1], chanserv
->nick
))
5298 reply("CSMSG_IS_CHANSERV");
5302 if(!(handle
= get_handle_info(argv
[1])))
5304 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5308 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5310 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5315 reply("CSMSG_USER_PRESENT", handle
->handle
);
5316 else if(uData
->seen
)
5317 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5319 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5321 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5322 reply("CSMSG_USER_VACATION", handle
->handle
);
5327 static MODCMD_FUNC(cmd_names
)
5329 struct userNode
*targ
;
5330 struct userData
*targData
;
5331 unsigned int ii
, pos
;
5334 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5336 targ
= channel
->members
.list
[ii
]->user
;
5337 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5340 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5343 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5347 if(IsUserSuspended(targData
))
5349 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5352 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5353 reply("CSMSG_END_NAMES", channel
->name
);
5358 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5360 switch(ntype
->visible_type
)
5362 case NOTE_VIS_ALL
: return 1;
5363 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5364 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5369 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5371 struct userData
*uData
;
5373 switch(ntype
->set_access_type
)
5375 case NOTE_SET_CHANNEL_ACCESS
:
5376 if(!user
->handle_info
)
5378 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5380 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5381 case NOTE_SET_CHANNEL_SETTER
:
5382 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5383 case NOTE_SET_PRIVILEGED
: default:
5384 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5388 static CHANSERV_FUNC(cmd_note
)
5390 struct chanData
*cData
;
5392 struct note_type
*ntype
;
5394 cData
= channel
->channel_info
;
5397 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5401 /* If no arguments, show all visible notes for the channel. */
5407 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5409 note
= iter_data(it
);
5410 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5413 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5414 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5417 reply("CSMSG_NOTELIST_END", channel
->name
);
5419 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5421 /* If one argument, show the named note. */
5424 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5425 && note_type_visible_to_user(cData
, note
->type
, user
))
5427 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5429 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5430 && note_type_visible_to_user(NULL
, ntype
, user
))
5432 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5437 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5441 /* Assume they're trying to set a note. */
5445 ntype
= dict_find(note_types
, argv
[1], NULL
);
5448 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5451 else if(note_type_settable_by_user(channel
, ntype
, user
))
5453 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5454 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5455 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5456 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5457 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5459 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5461 /* The note is viewable to staff only, so return 0
5462 to keep the invocation from getting logged (or
5463 regular users can see it in !events). */
5469 reply("CSMSG_NO_ACCESS");
5476 static CHANSERV_FUNC(cmd_delnote
)
5481 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5482 || !note_type_settable_by_user(channel
, note
->type
, user
))
5484 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5487 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5488 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5492 static CHANSERV_FUNC(cmd_last
)
5498 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5500 if(numoflines
< 1 || numoflines
> 200)
5502 reply("CSMSG_LAST_INVALID");
5505 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5509 static CHANSERV_FUNC(cmd_events
)
5511 struct logSearch discrim
;
5512 struct logReport report
;
5513 unsigned int matches
, limit
;
5515 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5516 if(limit
< 1 || limit
> 200)
5519 memset(&discrim
, 0, sizeof(discrim
));
5520 discrim
.masks
.bot
= chanserv
;
5521 discrim
.masks
.channel_name
= channel
->name
;
5523 discrim
.masks
.command
= argv
[2];
5524 discrim
.limit
= limit
;
5525 discrim
.max_time
= INT_MAX
;
5526 discrim
.severities
= 1 << LOG_COMMAND
;
5527 report
.reporter
= chanserv
;
5529 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5530 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5532 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5534 reply("MSG_MATCH_COUNT", matches
);
5536 reply("MSG_NO_MATCHES");
5540 static CHANSERV_FUNC(cmd_say
)
5546 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5547 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5549 else if(GetUserH(argv
[1]))
5552 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5553 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5557 reply("MSG_NOT_TARGET_NAME");
5563 static CHANSERV_FUNC(cmd_emote
)
5569 /* CTCP is so annoying. */
5570 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5571 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5573 else if(GetUserH(argv
[1]))
5575 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5576 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5580 reply("MSG_NOT_TARGET_NAME");
5586 struct channelList
*
5587 chanserv_support_channels(void)
5589 return &chanserv_conf
.support_channels
;
5592 static CHANSERV_FUNC(cmd_expire
)
5594 int channel_count
= registered_channels
;
5595 expire_channels(NULL
);
5596 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5601 chanserv_expire_suspension(void *data
)
5603 struct suspended
*suspended
= data
;
5604 struct chanNode
*channel
;
5606 if(!suspended
->expires
|| (now
< suspended
->expires
))
5607 suspended
->revoked
= now
;
5608 channel
= suspended
->cData
->channel
;
5609 suspended
->cData
->channel
= channel
;
5610 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5611 if(!IsOffChannel(suspended
->cData
))
5613 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5614 ss_cs_join_channel(channel
, 1);
5618 static CHANSERV_FUNC(cmd_csuspend
)
5620 struct suspended
*suspended
;
5621 char reason
[MAXLEN
];
5622 time_t expiry
, duration
;
5623 struct userData
*uData
;
5627 if(IsProtected(channel
->channel_info
))
5629 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5633 if(argv
[1][0] == '!')
5635 else if(IsSuspended(channel
->channel_info
))
5637 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5638 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5642 if(!strcmp(argv
[1], "0"))
5644 else if((duration
= ParseInterval(argv
[1])))
5645 expiry
= now
+ duration
;
5648 reply("MSG_INVALID_DURATION", argv
[1]);
5652 unsplit_string(argv
+ 2, argc
- 2, reason
);
5654 suspended
= calloc(1, sizeof(*suspended
));
5655 suspended
->revoked
= 0;
5656 suspended
->issued
= now
;
5657 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5658 suspended
->expires
= expiry
;
5659 suspended
->reason
= strdup(reason
);
5660 suspended
->cData
= channel
->channel_info
;
5661 suspended
->previous
= suspended
->cData
->suspended
;
5662 suspended
->cData
->suspended
= suspended
;
5664 if(suspended
->expires
)
5665 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5667 if(IsSuspended(channel
->channel_info
))
5669 suspended
->previous
->revoked
= now
;
5670 if(suspended
->previous
->expires
)
5671 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5673 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5674 channel
->name
, suspended
->suspender
);
5678 /* Mark all users in channel as absent. */
5679 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5688 /* Mark the channel as suspended, then part. */
5689 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5690 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5691 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5692 reply("CSMSG_SUSPENDED", channel
->name
);
5693 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5694 channel
->name
, suspended
->suspender
);
5699 static CHANSERV_FUNC(cmd_cunsuspend
)
5701 struct suspended
*suspended
;
5703 if(!IsSuspended(channel
->channel_info
))
5705 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5709 suspended
= channel
->channel_info
->suspended
;
5711 /* Expire the suspension and join ChanServ to the channel. */
5712 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5713 chanserv_expire_suspension(suspended
);
5714 reply("CSMSG_UNSUSPENDED", channel
->name
);
5715 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5716 channel
->name
, user
->handle_info
->handle
);
5720 typedef struct chanservSearch
5728 unsigned long flags
;
5732 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5735 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5740 search
= malloc(sizeof(struct chanservSearch
));
5741 memset(search
, 0, sizeof(*search
));
5744 for(i
= 0; i
< argc
; i
++)
5746 /* Assume all criteria require arguments. */
5749 reply("MSG_MISSING_PARAMS", argv
[i
]);
5753 if(!irccasecmp(argv
[i
], "name"))
5754 search
->name
= argv
[++i
];
5755 else if(!irccasecmp(argv
[i
], "registrar"))
5756 search
->registrar
= argv
[++i
];
5757 else if(!irccasecmp(argv
[i
], "unvisited"))
5758 search
->unvisited
= ParseInterval(argv
[++i
]);
5759 else if(!irccasecmp(argv
[i
], "registered"))
5760 search
->registered
= ParseInterval(argv
[++i
]);
5761 else if(!irccasecmp(argv
[i
], "flags"))
5764 if(!irccasecmp(argv
[i
], "nodelete"))
5765 search
->flags
|= CHANNEL_NODELETE
;
5766 else if(!irccasecmp(argv
[i
], "suspended"))
5767 search
->flags
|= CHANNEL_SUSPENDED
;
5770 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5774 else if(!irccasecmp(argv
[i
], "limit"))
5775 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5778 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5783 if(search
->name
&& !strcmp(search
->name
, "*"))
5785 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5786 search
->registrar
= 0;
5795 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5797 const char *name
= channel
->channel
->name
;
5798 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5799 (search
->registrar
&& !channel
->registrar
) ||
5800 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5801 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5802 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5803 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5810 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5812 struct chanData
*channel
;
5813 unsigned int matches
= 0;
5815 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5817 if(!chanserv_channel_match(channel
, search
))
5827 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5832 search_print(struct chanData
*channel
, void *data
)
5834 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5837 static CHANSERV_FUNC(cmd_search
)
5840 unsigned int matches
;
5841 channel_search_func action
;
5845 if(!irccasecmp(argv
[1], "count"))
5846 action
= search_count
;
5847 else if(!irccasecmp(argv
[1], "print"))
5848 action
= search_print
;
5851 reply("CSMSG_ACTION_INVALID", argv
[1]);
5855 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5859 if(action
== search_count
)
5860 search
->limit
= INT_MAX
;
5862 if(action
== search_print
)
5864 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5865 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5869 matches
= chanserv_channel_search(search
, action
, user
);
5872 reply("MSG_MATCH_COUNT", matches
);
5874 reply("MSG_NO_MATCHES");
5880 static CHANSERV_FUNC(cmd_unvisited
)
5882 struct chanData
*cData
;
5883 time_t interval
= chanserv_conf
.channel_expire_delay
;
5884 char buffer
[INTERVALLEN
];
5885 unsigned int limit
= 25, matches
= 0;
5889 interval
= ParseInterval(argv
[1]);
5891 limit
= atoi(argv
[2]);
5894 intervalString(buffer
, interval
, user
->handle_info
);
5895 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5897 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5899 if((now
- cData
->visited
) < interval
)
5902 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5903 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5910 static MODCMD_FUNC(chan_opt_defaulttopic
)
5916 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5918 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5922 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5924 free(channel
->channel_info
->topic
);
5925 if(topic
[0] == '*' && topic
[1] == 0)
5927 topic
= channel
->channel_info
->topic
= NULL
;
5931 topic
= channel
->channel_info
->topic
= strdup(topic
);
5932 if(channel
->channel_info
->topic_mask
5933 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5934 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5936 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5939 if(channel
->channel_info
->topic
)
5940 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5942 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5946 static MODCMD_FUNC(chan_opt_topicmask
)
5950 struct chanData
*cData
= channel
->channel_info
;
5953 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5955 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5959 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5961 if(cData
->topic_mask
)
5962 free(cData
->topic_mask
);
5963 if(mask
[0] == '*' && mask
[1] == 0)
5965 cData
->topic_mask
= 0;
5969 cData
->topic_mask
= strdup(mask
);
5971 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5972 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5973 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5977 if(channel
->channel_info
->topic_mask
)
5978 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5980 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5984 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5988 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5992 if(greeting
[0] == '*' && greeting
[1] == 0)
5996 unsigned int length
= strlen(greeting
);
5997 if(length
> chanserv_conf
.greeting_length
)
5999 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
6002 *data
= strdup(greeting
);
6011 reply(name
, user_find_message(user
, "MSG_NONE"));
6015 static MODCMD_FUNC(chan_opt_greeting
)
6017 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
6020 static MODCMD_FUNC(chan_opt_usergreeting
)
6022 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
6025 static MODCMD_FUNC(chan_opt_maxsetinfo
)
6027 unsigned int charmax
;
6030 charmax
= atoi(argv
[1]);
6031 if ((charmax
> 0) && (charmax
< chanserv_conf
.max_userinfo_length
))
6032 channel
->channel_info
->maxsetinfo
= charmax
;
6035 reply("CSMSG_SET_MAXSETINFO", channel
->channel_info
->maxsetinfo
);
6039 static MODCMD_FUNC(chan_opt_modes
)
6041 struct mod_chanmode
*new_modes
;
6042 char modes
[MODELEN
];
6046 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
6047 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6051 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
6053 reply("CSMSG_NO_ACCESS");
6056 if(argv
[1][0] == '*' && argv
[1][1] == 0)
6058 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
6060 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
6062 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6065 else if(new_modes
->argc
> 1)
6067 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
6068 mod_chanmode_free(new_modes
);
6073 channel
->channel_info
->modes
= *new_modes
;
6074 modcmd_chanmode_announce(new_modes
);
6075 mod_chanmode_free(new_modes
);
6079 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
6081 reply("CSMSG_SET_MODES", modes
);
6083 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
6087 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6089 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6091 struct chanData
*cData
= channel
->channel_info
;
6096 /* Set flag according to value. */
6097 if(enabled_string(argv
[1]))
6099 cData
->flags
|= mask
;
6102 else if(disabled_string(argv
[1]))
6104 cData
->flags
&= ~mask
;
6109 reply("MSG_INVALID_BINARY", argv
[1]);
6115 /* Find current option value. */
6116 value
= (cData
->flags
& mask
) ? 1 : 0;
6120 reply(name
, user_find_message(user
, "MSG_ON"));
6122 reply(name
, user_find_message(user
, "MSG_OFF"));
6126 static MODCMD_FUNC(chan_opt_nodelete
)
6128 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
6130 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
6134 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
6137 static MODCMD_FUNC(chan_opt_dynlimit
)
6139 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
6142 static MODCMD_FUNC(chan_opt_offchannel
)
6144 struct chanData
*cData
= channel
->channel_info
;
6149 /* Set flag according to value. */
6150 if(enabled_string(argv
[1]))
6152 if(!IsOffChannel(cData
))
6153 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
6154 cData
->flags
|= CHANNEL_OFFCHANNEL
;
6157 else if(disabled_string(argv
[1]))
6159 if(IsOffChannel(cData
))
6161 struct mod_chanmode change
;
6162 mod_chanmode_init(&change
);
6164 change
.args
[0].mode
= MODE_CHANOP
;
6165 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
6166 mod_chanmode_announce(chanserv
, channel
, &change
);
6168 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
6173 reply("MSG_INVALID_BINARY", argv
[1]);
6179 /* Find current option value. */
6180 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6184 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6186 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6190 static MODCMD_FUNC(chan_opt_defaults
)
6192 struct userData
*uData
;
6193 struct chanData
*cData
;
6194 const char *confirm
;
6195 enum levelOption lvlOpt
;
6196 enum charOption chOpt
;
6198 cData
= channel
->channel_info
;
6199 uData
= GetChannelUser(cData
, user
->handle_info
);
6200 if(!uData
|| (uData
->access
< UL_OWNER
))
6202 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6205 confirm
= make_confirmation_string(uData
);
6206 if((argc
< 2) || strcmp(argv
[1], confirm
))
6208 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6211 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6212 cData
->modes
= chanserv_conf
.default_modes
;
6213 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6214 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6215 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6216 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6217 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6222 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6224 struct chanData
*cData
= channel
->channel_info
;
6225 struct userData
*uData
;
6226 unsigned short value
;
6230 if(!check_user_level(channel
, user
, option
, 1, 1))
6232 reply("CSMSG_CANNOT_SET");
6235 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6236 if(!value
&& strcmp(argv
[1], "0"))
6238 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6241 uData
= GetChannelUser(cData
, user
->handle_info
);
6242 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6244 reply("CSMSG_BAD_SETLEVEL");
6250 /* This test only applies to owners, since non-owners
6251 * trying to set an option to above their level get caught
6252 * by the CSMSG_BAD_SETLEVEL test above.
6254 if(value
> uData
->access
)
6256 reply("CSMSG_BAD_SETTERS");
6263 cData
->lvlOpts
[option
] = value
;
6265 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6269 static MODCMD_FUNC(chan_opt_enfops
)
6271 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6274 static MODCMD_FUNC(chan_opt_enfhalfops
)
6276 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6278 static MODCMD_FUNC(chan_opt_enfmodes
)
6280 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6283 static MODCMD_FUNC(chan_opt_enftopic
)
6285 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6288 static MODCMD_FUNC(chan_opt_pubcmd
)
6290 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6293 static MODCMD_FUNC(chan_opt_setters
)
6295 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6298 static MODCMD_FUNC(chan_opt_userinfo
)
6300 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6303 static MODCMD_FUNC(chan_opt_topicsnarf
)
6305 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6308 static MODCMD_FUNC(chan_opt_inviteme
)
6310 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6313 /* TODO: Make look like this when no args are
6315 * -X3- -------------------------------
6316 * -X3- BanTimeout: Bans are removed:
6317 * -X3- ----- * indicates current -----
6318 * -X3- 0: [*] Never.
6319 * -X3- 1: [ ] After 10 minutes.
6320 * -X3- 2: [ ] After 2 hours.
6321 * -X3- 3: [ ] After 4 hours.
6322 * -X3- 4: [ ] After 24 hours.
6323 * -X3- 5: [ ] After one week.
6324 * -X3- ------------- End -------------
6327 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6329 struct chanData
*cData
= channel
->channel_info
;
6330 int count
= charOptions
[option
].count
, index
;
6334 index
= atoi(argv
[1]);
6336 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6338 reply("CSMSG_INVALID_NUMERIC", index
);
6339 /* Show possible values. */
6340 for(index
= 0; index
< count
; index
++)
6341 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6345 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6349 /* Find current option value. */
6352 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6356 /* Somehow, the option value is corrupt; reset it to the default. */
6357 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6362 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6366 static MODCMD_FUNC(chan_opt_automode
)
6368 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6371 static MODCMD_FUNC(chan_opt_protect
)
6373 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6376 static MODCMD_FUNC(chan_opt_toys
)
6378 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6381 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6383 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6386 static MODCMD_FUNC(chan_opt_bantimeout
)
6388 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6391 static MODCMD_FUNC(chan_opt_topicrefresh
)
6393 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6396 static MODCMD_FUNC(chan_opt_resync
)
6398 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6401 static struct svccmd_list set_shows_list
;
6404 handle_svccmd_unbind(struct svccmd
*target
) {
6406 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6407 if(target
== set_shows_list
.list
[ii
])
6408 set_shows_list
.used
= 0;
6411 static CHANSERV_FUNC(cmd_set
)
6413 struct svccmd
*subcmd
;
6417 /* Check if we need to (re-)initialize set_shows_list. */
6418 if(!set_shows_list
.used
)
6420 if(!set_shows_list
.size
)
6422 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6423 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6425 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6427 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6428 sprintf(buf
, "%s %s", argv
[0], name
);
6429 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6432 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6435 svccmd_list_append(&set_shows_list
, subcmd
);
6441 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6442 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6444 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6446 subcmd
= set_shows_list
.list
[ii
];
6447 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6449 reply("CSMSG_CHANNEL_OPTIONS_END");
6453 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6454 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6457 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6460 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6462 reply("CSMSG_NO_ACCESS");
6466 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6470 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6472 struct userData
*uData
;
6474 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6477 reply("CSMSG_NOT_USER", channel
->name
);
6483 /* Just show current option value. */
6485 else if(enabled_string(argv
[1]))
6487 uData
->flags
|= mask
;
6489 else if(disabled_string(argv
[1]))
6491 uData
->flags
&= ~mask
;
6495 reply("MSG_INVALID_BINARY", argv
[1]);
6499 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6503 static MODCMD_FUNC(user_opt_autoop
)
6505 struct userData
*uData
;
6507 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6510 reply("CSMSG_NOT_USER", channel
->name
);
6513 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6514 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6516 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6519 static MODCMD_FUNC(user_opt_autoinvite
)
6521 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6524 static MODCMD_FUNC(user_opt_autojoin
)
6526 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6529 static MODCMD_FUNC(user_opt_info
)
6531 struct userData
*uData
;
6534 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6538 /* If they got past the command restrictions (which require access)
6539 * but fail this test, we have some fool with security override on.
6541 reply("CSMSG_NOT_USER", channel
->name
);
6548 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6549 if(strlen(infoline
) > channel
->channel_info
->maxsetinfo
)
6551 reply("CSMSG_INFOLINE_TOO_LONG", channel
->channel_info
->maxsetinfo
);
6554 bp
= strcspn(infoline
, "\001");
6557 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6562 if(infoline
[0] == '*' && infoline
[1] == 0)
6565 uData
->info
= strdup(infoline
);
6568 reply("CSMSG_USET_INFO", uData
->info
);
6570 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6574 struct svccmd_list uset_shows_list
;
6576 static CHANSERV_FUNC(cmd_uset
)
6578 struct svccmd
*subcmd
;
6582 /* Check if we need to (re-)initialize uset_shows_list. */
6583 if(!uset_shows_list
.used
)
6587 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6590 if(!uset_shows_list
.size
)
6592 uset_shows_list
.size
= ArrayLength(options
);
6593 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6595 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6597 const char *name
= options
[ii
];
6598 sprintf(buf
, "%s %s", argv
[0], name
);
6599 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6602 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6605 svccmd_list_append(&uset_shows_list
, subcmd
);
6611 /* Do this so options are presented in a consistent order. */
6612 reply("CSMSG_USER_OPTIONS");
6613 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6614 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6618 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6619 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6622 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6626 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6629 static CHANSERV_FUNC(cmd_giveownership
)
6631 struct handle_info
*new_owner_hi
;
6632 struct userData
*new_owner
, *curr_user
;
6633 struct chanData
*cData
= channel
->channel_info
;
6634 struct do_not_register
*dnr
;
6635 struct giveownership
*giveownership
;
6636 unsigned int force
, override
;
6637 unsigned short co_access
, new_owner_old_access
;
6638 char transfer_reason
[MAXLEN
];
6641 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6642 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6644 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6645 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6646 && (uData
->access
> 500)
6647 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6648 || uData
->access
< 500));
6651 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6653 struct userData
*owner
= NULL
;
6654 for(curr_user
= channel
->channel_info
->users
;
6656 curr_user
= curr_user
->next
)
6658 if(curr_user
->access
!= UL_OWNER
)
6662 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6669 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6671 char delay
[INTERVALLEN
];
6672 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6673 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6677 reply("CSMSG_NO_OWNER", channel
->name
);
6680 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6682 if(new_owner_hi
== user
->handle_info
)
6684 reply("CSMSG_NO_TRANSFER_SELF");
6687 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6692 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
, 0);
6696 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6700 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6702 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6705 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6706 if(!IsHelping(user
))
6707 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6709 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6713 new_owner_old_access
= new_owner
->access
;
6714 if(new_owner
->access
>= UL_COOWNER
)
6715 co_access
= new_owner
->access
;
6717 co_access
= UL_COOWNER
;
6718 new_owner
->access
= UL_OWNER
;
6720 curr_user
->access
= co_access
;
6721 cData
->ownerTransfer
= now
;
6723 giveownership
= calloc(1, sizeof(*giveownership
));
6724 giveownership
->issued
= now
;
6725 giveownership
->old_owner
= strdup(curr_user
->handle
->handle
);
6726 giveownership
->target
= strdup(new_owner_hi
->handle
);
6727 giveownership
->target_access
= new_owner_old_access
;
6730 if(argc
> (2 + force
))
6732 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6733 giveownership
->reason
= strdup(transfer_reason
);
6735 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6738 giveownership
->previous
= channel
->channel_info
->giveownership
;
6739 channel
->channel_info
->giveownership
= giveownership
;
6741 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6742 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6743 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6748 chanserv_expire_user_suspension(void *data
)
6750 struct userData
*target
= data
;
6752 target
->expires
= 0;
6753 target
->flags
&= ~USER_SUSPENDED
;
6756 static CHANSERV_FUNC(cmd_suspend
)
6758 struct handle_info
*hi
;
6759 struct userData
*self
, *target
;
6763 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6764 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6765 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6767 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6770 if(target
->access
>= self
->access
)
6772 reply("MSG_USER_OUTRANKED", hi
->handle
);
6775 if(target
->flags
& USER_SUSPENDED
)
6777 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6782 target
->present
= 0;
6785 if(!strcmp(argv
[2], "0"))
6789 unsigned int duration
;
6790 if(!(duration
= ParseInterval(argv
[2])))
6792 reply("MSG_INVALID_DURATION", argv
[2]);
6795 expiry
= now
+ duration
;
6798 target
->expires
= expiry
;
6801 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6803 target
->flags
|= USER_SUSPENDED
;
6804 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6808 static CHANSERV_FUNC(cmd_unsuspend
)
6810 struct handle_info
*hi
;
6811 struct userData
*self
, *target
;
6814 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6815 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6816 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6818 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6821 if(target
->access
>= self
->access
)
6823 reply("MSG_USER_OUTRANKED", hi
->handle
);
6826 if(!(target
->flags
& USER_SUSPENDED
))
6828 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6831 target
->flags
&= ~USER_SUSPENDED
;
6832 scan_user_presence(target
, NULL
);
6833 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6834 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6838 static MODCMD_FUNC(cmd_deleteme
)
6840 struct handle_info
*hi
;
6841 struct userData
*target
;
6842 const char *confirm_string
;
6843 unsigned short access
;
6846 hi
= user
->handle_info
;
6847 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6849 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6852 if(target
->access
== UL_OWNER
)
6854 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6857 confirm_string
= make_confirmation_string(target
);
6858 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6860 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6863 access
= target
->access
;
6864 channel_name
= strdup(channel
->name
);
6865 del_channel_user(target
, 1);
6866 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6872 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6874 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6875 struct chanData
*cData
;
6878 for(cData
= channelList
; cData
; cData
= cData
->next
)
6880 if(IsSuspended(cData
))
6882 opt
= cData
->chOpts
[chTopicRefresh
];
6885 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6888 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6889 cData
->last_refresh
= refresh_num
;
6891 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6895 chanserv_auto_resync(UNUSED_ARG(void *data
))
6897 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6898 struct chanData
*cData
;
6901 for(cData
= channelList
; cData
; cData
= cData
->next
)
6903 if(IsSuspended(cData
)) continue;
6904 opt
= cData
->chOpts
[chResync
];
6905 if(opt
== 'n') continue;
6906 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6907 resync_channel(cData
->channel
);
6908 cData
->last_resync
= refresh_num
;
6910 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6913 static CHANSERV_FUNC(cmd_unf
)
6917 char response
[MAXLEN
];
6918 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6919 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6920 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6923 reply("CSMSG_UNF_RESPONSE");
6927 static CHANSERV_FUNC(cmd_ping
)
6931 char response
[MAXLEN
];
6932 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6933 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6934 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6937 reply("CSMSG_PING_RESPONSE");
6941 static CHANSERV_FUNC(cmd_wut
)
6945 char response
[MAXLEN
];
6946 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6947 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6948 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6951 reply("CSMSG_WUT_RESPONSE");
6956 static CHANSERV_FUNC(cmd_8ball
)
6958 unsigned int i
, j
, accum
;
6963 for(i
=1; i
<argc
; i
++)
6964 for(j
=0; argv
[i
][j
]; j
++)
6965 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6966 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6969 char response
[MAXLEN
];
6970 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6971 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6974 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6978 #else /* Use cool 8ball instead */
6980 void eightball(char *outcome
, int method
, unsigned int seed
)
6984 #define NUMOFCOLORS 18
6985 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6986 "white", "black", "grey", "brown",
6987 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6988 "fuchsia","turquoise","magenta", "cyan"};
6989 #define NUMOFLOCATIONS 50
6990 char balllocations
[50][55] = {
6991 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6992 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6993 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6994 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6995 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6996 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6997 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6998 "your bra", "your hair", "your bed", "the couch", "the wall",
6999 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7000 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7001 #define NUMOFPREPS 15
7002 char ballpreps
[50][50] = {
7003 "Near", "Somewhere near", "In", "In", "In",
7004 "In", "Hiding in", "Under", "Next to", "Over",
7005 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7006 #define NUMOFNUMS 34
7007 char ballnums
[50][50] = {
7008 "A hundred", "A thousand", "A few", "42",
7009 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7010 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7011 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7013 #define NUMOFMULTS 8
7014 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7017 * 0: normal (Not used in x3)
7024 if (method
== 1) /* A Color */
7028 answer
= (rand() % 12); /* Make sure this is the # of entries */
7031 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
7033 case 1: strcpy(tmp
, "Sort of a light %s color.");
7035 case 2: strcpy(tmp
, "Dark and dreary %s.");
7037 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
7039 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
7041 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
7043 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
7045 case 10: strcpy(tmp
, "Solid %s.");
7047 case 11: strcpy(tmp
, "Transparent %s.");
7049 default: strcpy(outcome
, "An invalid random number was generated.");
7052 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
7055 else if (method
== 2) /* Location */
7057 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
7059 else if (method
== 3) /* Number of ___ */
7061 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
7065 //Debug(DBGWARNING, "Error in 8ball.");
7070 static CHANSERV_FUNC(cmd_8ball
)
7072 char *word1
, *word2
, *word3
;
7073 static char eb
[MAXLEN
];
7074 unsigned int accum
, i
, j
;
7078 for(i
=1; i
<argc
; i
++)
7079 for(j
=0; argv
[i
][j
]; j
++)
7080 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
7082 accum
+= time(NULL
)/3600;
7084 word2
= argc
>2?argv
[2]:"";
7085 word3
= argc
>3?argv
[3]:"";
7088 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
7089 eightball(eb
, 1, accum
);
7090 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7091 eightball(eb
, 1, accum
);
7092 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
7093 eightball(eb
, 1, accum
);
7094 /*** LOCATION *****/
7099 (strcasecmp(word1
, "where") == 0) &&
7100 (strcasecmp(word2
, "is") == 0)
7104 strcasecmp(word1
, "where's") == 0
7107 eightball(eb
, 2, accum
);
7109 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
7110 eightball(eb
, 3, accum
);
7114 /* Generic 8ball question.. so pull from x3.conf srvx style */
7117 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
7120 char response
[MAXLEN
];
7121 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
7122 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7125 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
7131 char response
[MAXLEN
];
7132 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
7133 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
7136 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
7141 static CHANSERV_FUNC(cmd_d
)
7143 unsigned long sides
, count
, modifier
, ii
, total
;
7144 char response
[MAXLEN
], *sep
;
7148 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
7158 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
7159 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
7163 else if((sep
[0] == '-') && isdigit(sep
[1]))
7164 modifier
= strtoul(sep
, NULL
, 10);
7165 else if((sep
[0] == '+') && isdigit(sep
[1]))
7166 modifier
= strtoul(sep
+1, NULL
, 10);
7173 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
7178 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7181 for(total
= ii
= 0; ii
< count
; ++ii
)
7182 total
+= (rand() % sides
) + 1;
7185 if((count
> 1) || modifier
)
7187 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7188 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7192 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7193 sprintf(response
, fmt
, total
, sides
);
7196 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7198 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7202 static CHANSERV_FUNC(cmd_huggle
)
7204 /* CTCP must be via PRIVMSG, never notice */
7206 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7208 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7212 static CHANSERV_FUNC(cmd_calc
)
7214 char response
[MAXLEN
];
7217 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7220 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7222 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7226 static CHANSERV_FUNC(cmd_reply
)
7230 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7233 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7235 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7240 chanserv_adjust_limit(void *data
)
7242 struct mod_chanmode change
;
7243 struct chanData
*cData
= data
;
7244 struct chanNode
*channel
= cData
->channel
;
7247 if(IsSuspended(cData
))
7250 cData
->limitAdjusted
= now
;
7251 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7252 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7254 if(limit
> cData
->modes
.new_limit
)
7255 limit
= cData
->modes
.new_limit
;
7256 else if(limit
== cData
->modes
.new_limit
)
7260 mod_chanmode_init(&change
);
7261 change
.modes_set
= MODE_LIMIT
;
7262 change
.new_limit
= limit
;
7263 mod_chanmode_announce(chanserv
, channel
, &change
);
7267 handle_new_channel(struct chanNode
*channel
)
7269 struct chanData
*cData
;
7271 if(!(cData
= channel
->channel_info
))
7274 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7275 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7277 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7278 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7282 trace_check_bans(struct userNode
*user
, struct chanNode
*chan
)
7284 struct banData
*bData
;
7285 struct mod_chanmode
*change
;
7287 change
= find_matching_bans(&chan
->banlist
, user
, NULL
);
7292 if (chan
->channel_info
) {
7293 for(bData
= chan
->channel_info
->bans
; bData
; bData
= bData
->next
) {
7295 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
))
7307 check_bans(struct userNode
*user
, const char *channel
)
7309 struct chanNode
*chan
;
7310 struct mod_chanmode change
;
7311 struct chanData
*cData
;
7312 struct banData
*bData
;
7314 if (!(chan
= GetChannel(channel
)))
7317 if(!(cData
= chan
->channel_info
))
7320 mod_chanmode_init(&change
);
7323 if(chan
->banlist
.used
< MAXBANS
)
7325 /* Not joining through a ban. */
7326 for(bData
= cData
->bans
;
7327 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7328 bData
= bData
->next
);
7332 char kick_reason
[MAXLEN
];
7333 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7335 bData
->triggered
= now
;
7336 if(bData
!= cData
->bans
)
7338 /* Shuffle the ban to the head of the list. */
7340 bData
->next
->prev
= bData
->prev
;
7342 bData
->prev
->next
= bData
->next
;
7345 bData
->next
= cData
->bans
;
7348 cData
->bans
->prev
= bData
;
7350 cData
->bans
= bData
;
7353 change
.args
[0].mode
= MODE_BAN
;
7354 change
.args
[0].u
.hostmask
= bData
->mask
;
7355 mod_chanmode_announce(chanserv
, chan
, &change
);
7356 KickChannelUser(user
, chan
, chanserv
, kick_reason
);
7364 channel_user_is_exempt(struct userNode
*user
, struct chanNode
*channel
)
7367 for(ii
= 0; ii
< channel
->exemptlist
.used
; ii
++)
7369 if(user_matches_glob(user
, channel
->exemptlist
.list
[ii
]->exempt
, MATCH_USENICK
))
7376 /* Welcome to my worst nightmare. Warning: Read (or modify)
7377 the code below at your own risk. */
7379 handle_join(struct modeNode
*mNode
)
7381 struct mod_chanmode change
;
7382 struct userNode
*user
= mNode
->user
;
7383 struct chanNode
*channel
= mNode
->channel
;
7384 struct chanData
*cData
;
7385 struct userData
*uData
= NULL
;
7386 struct banData
*bData
;
7387 struct handle_info
*handle
;
7388 unsigned int modes
= 0, info
= 0;
7391 if(IsLocal(user
) || !channel
|| !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7394 cData
= channel
->channel_info
;
7395 if(channel
->members
.used
> cData
->max
)
7396 cData
->max
= channel
->members
.used
;
7399 /* Check for bans. If they're joining through a ban, one of two
7401 * 1: Join during a netburst, by riding the break. Kick them
7402 * unless they have ops or voice in the channel.
7403 * 2: They're allowed to join through the ban (an invite in
7404 * ircu2.10, or a +e on Hybrid, or something).
7405 * If they're not joining through a ban, and the banlist is not
7406 * full, see if they're on the banlist for the channel. If so,
7409 if(user
->uplink
->burst
&& !mNode
->modes
)
7412 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7414 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7416 /* Riding a netburst. Naughty. */
7417 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7424 if(user
->handle_info
)
7426 handle
= user
->handle_info
;
7429 uData
= GetTrueChannelAccess(cData
, handle
);
7434 mod_chanmode_init(&change
);
7437 /* TODO: maybe only people above inviteme level? -Rubin */
7438 /* We don't kick people with access */
7439 if(!uData
&& !channel_user_is_exempt(user
, channel
))
7441 if(channel
->banlist
.used
< MAXBANS
)
7443 /* Not joining through a ban. */
7444 for(bData
= cData
->bans
;
7445 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7446 bData
= bData
->next
);
7450 char kick_reason
[MAXLEN
];
7451 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7453 bData
->triggered
= now
;
7454 if(bData
!= cData
->bans
)
7456 /* Shuffle the ban to the head of the list. */
7458 bData
->next
->prev
= bData
->prev
;
7460 bData
->prev
->next
= bData
->next
;
7463 bData
->next
= cData
->bans
;
7466 cData
->bans
->prev
= bData
;
7467 cData
->bans
= bData
;
7470 change
.args
[0].mode
= MODE_BAN
;
7471 change
.args
[0].u
.hostmask
= bData
->mask
;
7472 mod_chanmode_announce(chanserv
, channel
, &change
);
7473 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7479 /* ChanServ will not modify the limits in join-flooded channels.
7480 It will also skip DynLimit processing when the user (or srvx)
7481 is bursting in, because there are likely more incoming. */
7482 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7483 && !user
->uplink
->burst
7484 && !channel
->join_flooded
7485 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7487 /* The user count has begun "bumping" into the channel limit,
7488 so set a timer to raise the limit a bit. Any previous
7489 timers are removed so three incoming users within the delay
7490 results in one limit change, not three. */
7492 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7493 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7496 /* Give automodes exept during join-floods */
7497 if(!channel
->join_flooded
)
7499 if(cData
->chOpts
[chAutomode
] == 'v')
7500 modes
|= MODE_VOICE
;
7501 else if(cData
->chOpts
[chAutomode
] == 'h')
7502 modes
|= MODE_HALFOP
;
7503 else if(cData
->chOpts
[chAutomode
] == 'o')
7504 modes
|= MODE_CHANOP
;
7507 greeting
= cData
->greeting
;
7508 if(user
->handle_info
)
7510 /* handle = user->handle_info; */
7512 if(IsHelper(user
) && !IsHelping(user
))
7515 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7517 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7519 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7525 /* uData = GetTrueChannelAccess(cData, handle); */
7526 if(uData
&& !IsUserSuspended(uData
))
7528 /* non users getting automodes are handled above. */
7529 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7531 /* just op everyone with access */
7532 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7533 modes
|= MODE_VOICE
;
7534 /* or do their access level */
7535 else if(uData
->access
>= UL_OP
)
7536 modes
|= MODE_CHANOP
;
7537 else if(uData
->access
>= UL_HALFOP
)
7538 modes
|= MODE_HALFOP
;
7539 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7540 modes
|= MODE_VOICE
;
7542 if(uData
->access
>= UL_PRESENT
)
7543 cData
->visited
= now
;
7544 if(cData
->user_greeting
)
7545 greeting
= cData
->user_greeting
;
7547 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7548 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7556 /* If user joining normally (not during burst), apply op or voice,
7557 * and send greeting/userinfo as appropriate.
7559 if(!user
->uplink
->burst
)
7563 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7564 if(modes & MODE_CHANOP) {
7565 modes &= ~MODE_HALFOP;
7566 modes &= ~MODE_VOICE;
7569 change
.args
[0].mode
= modes
;
7570 change
.args
[0].u
.member
= mNode
;
7571 mod_chanmode_announce(chanserv
, channel
, &change
);
7574 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7576 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7582 chanserv_autojoin_channels(struct userNode
*user
)
7584 struct userData
*channel
;
7586 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7588 struct chanNode
*cn
;
7589 struct modeNode
*mn
;
7591 if(IsUserSuspended(channel
)
7592 || IsSuspended(channel
->channel
)
7593 || !(cn
= channel
->channel
->channel
))
7596 mn
= GetUserMode(cn
, user
);
7599 if(!IsUserSuspended(channel
)
7600 && IsUserAutoJoin(channel
)
7601 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7603 && !user
->uplink
->burst
)
7604 irc_svsjoin(chanserv
, user
, cn
);
7610 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7612 struct mod_chanmode change
;
7613 struct userData
*channel
;
7614 unsigned int ii
, jj
, i
;
7616 if(!user
->handle_info
)
7619 mod_chanmode_init(&change
);
7621 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7623 struct chanNode
*cn
;
7624 struct chanData
*cData
;
7625 struct modeNode
*mn
;
7626 if(IsUserSuspended(channel
)
7627 || IsSuspended(channel
->channel
)
7628 || !(cn
= channel
->channel
->channel
))
7631 cData
= cn
->channel_info
;
7632 mn
= GetUserMode(cn
, user
);
7635 if(!IsUserSuspended(channel
)
7636 && IsUserAutoInvite(channel
)
7637 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7639 && !user
->uplink
->burst
)
7640 irc_invite(chanserv
, user
, cn
);
7644 if(channel
->access
>= UL_PRESENT
)
7645 channel
->channel
->visited
= now
;
7647 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
7649 if(channel
->access
>= UL_OP
)
7650 change
.args
[0].mode
= MODE_CHANOP
;
7651 else if(channel
->access
>= UL_HALFOP
)
7652 change
.args
[0].mode
= MODE_HALFOP
;
7653 else if(channel
->access
>= UL_PEON
)
7654 change
.args
[0].mode
= MODE_VOICE
;
7656 change
.args
[0].mode
= 0;
7657 change
.args
[0].u
.member
= mn
;
7658 if(change
.args
[0].mode
)
7659 mod_chanmode_announce(chanserv
, cn
, &change
);
7662 channel
->seen
= now
;
7663 channel
->present
= 1;
7666 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7668 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7669 struct banData
*ban
;
7671 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7672 || !channel
->channel_info
7673 || IsSuspended(channel
->channel_info
))
7675 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7677 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7678 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7680 if(jj
< channel
->banlist
.used
)
7682 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7684 char kick_reason
[MAXLEN
];
7685 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7687 change
.args
[0].mode
= MODE_BAN
;
7688 change
.args
[0].u
.hostmask
= ban
->mask
;
7689 mod_chanmode_announce(chanserv
, channel
, &change
);
7690 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7691 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7692 ban
->triggered
= now
;
7697 if(IsSupportHelper(user
))
7699 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7701 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7703 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7709 if (user
->handle_info
->ignores
->used
) {
7710 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7711 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7715 if (user
->handle_info
->epithet
)
7716 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7718 /* process autojoin channels 5 seconds later as this sometimes
7719 happens before autohide */
7720 // timeq_add(now + 5, chanserv_autojoin_channels, user);
7721 chanserv_autojoin_channels(user
);
7725 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7727 struct chanData
*cData
;
7728 struct userData
*uData
;
7730 cData
= mn
->channel
->channel_info
;
7731 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7734 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7736 /* Allow for a bit of padding so that the limit doesn't
7737 track the user count exactly, which could get annoying. */
7738 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7740 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7741 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7745 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7747 scan_user_presence(uData
, mn
->user
);
7751 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7753 unsigned int ii
, jj
;
7754 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7756 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7757 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7759 if(jj
< mn
->user
->channels
.used
)
7762 if(ii
== chanserv_conf
.support_channels
.used
)
7763 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7768 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7770 struct userData
*uData
;
7772 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7773 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7774 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7777 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
7779 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7780 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7783 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7788 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7790 struct chanData
*cData
;
7792 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7795 cData
= channel
->channel_info
;
7796 if(bad_topic(channel
, user
, channel
->topic
))
7797 { /* User doesnt have privs to set topics. Undo it */
7798 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7799 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7802 /* If there is a topic mask set, and the new topic doesnt match,
7803 * set the topic to mask + new_topic */
7804 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7806 char new_topic
[TOPICLEN
+1];
7807 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7810 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7811 /* and fall through to topicsnarf code below.. */
7813 else /* Topic couldnt fit into mask, was too long */
7815 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7816 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7817 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7821 /* With topicsnarf, grab the topic and save it as the default topic. */
7822 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7825 cData
->topic
= strdup(channel
->topic
);
7831 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7833 struct mod_chanmode
*bounce
= NULL
;
7834 unsigned int bnc
, ii
;
7837 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7840 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7841 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7843 char correct
[MAXLEN
];
7844 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7845 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7846 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7848 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7850 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7852 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7853 if(!protect_user(victim
, user
, channel
->channel_info
, false))
7856 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7859 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7860 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7861 if(bounce
->args
[bnc
].u
.member
)
7865 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7866 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7868 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7870 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7872 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7873 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7876 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7877 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7878 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7881 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7883 const char *ban
= change
->args
[ii
].u
.hostmask
;
7884 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7887 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7888 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7889 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7891 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7896 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7897 mod_chanmode_announce(chanserv
, channel
, bounce
);
7898 for(ii
= 0; ii
< change
->argc
; ++ii
)
7899 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7900 free((char*)bounce
->args
[ii
].u
.hostmask
);
7901 mod_chanmode_free(bounce
);
7906 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7908 struct chanNode
*channel
;
7909 struct banData
*bData
;
7910 struct mod_chanmode change
;
7911 unsigned int ii
, jj
;
7912 char kick_reason
[MAXLEN
];
7914 mod_chanmode_init(&change
);
7916 change
.args
[0].mode
= MODE_BAN
;
7917 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7919 channel
= user
->channels
.list
[ii
]->channel
;
7920 /* Need not check for bans if they're opped or voiced. */
7921 /* TODO: does this make sense in automode v, h, and o? *
7922 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7923 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7925 /* Need not check for bans unless channel registration is active. */
7926 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7928 /* Look for a matching ban already on the channel. */
7929 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7930 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7932 /* Need not act if we found one. */
7933 if(jj
< channel
->banlist
.used
)
7935 /* don't kick someone on the userlist */
7936 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7938 /* Look for a matching ban in this channel. */
7939 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7941 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7943 change
.args
[0].u
.hostmask
= bData
->mask
;
7944 mod_chanmode_announce(chanserv
, channel
, &change
);
7945 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7946 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7947 bData
->triggered
= now
;
7948 break; /* we don't need to check any more bans in the channel */
7953 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7955 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7959 dict_remove2(handle_dnrs
, old_handle
, 1);
7960 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7961 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7966 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7968 struct userNode
*h_user
;
7970 if(handle
->channels
)
7972 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7973 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7975 while(handle
->channels
)
7976 del_channel_user(handle
->channels
, 1);
7981 handle_server_link(UNUSED_ARG(struct server
*server
))
7983 struct chanData
*cData
;
7985 for(cData
= channelList
; cData
; cData
= cData
->next
)
7987 if(!IsSuspended(cData
))
7988 cData
->may_opchan
= 1;
7989 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7990 && !cData
->channel
->join_flooded
7991 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7992 < chanserv_conf
.adjust_threshold
))
7994 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7995 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
8001 chanserv_conf_read(void)
8005 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
8006 struct mod_chanmode
*change
;
8007 struct string_list
*strlist
;
8008 struct chanNode
*chan
;
8011 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
8013 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
8016 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8017 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8018 chanserv_conf
.support_channels
.used
= 0;
8019 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
8021 for(ii
= 0; ii
< strlist
->used
; ++ii
)
8023 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8026 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
8028 channelList_append(&chanserv_conf
.support_channels
, chan
);
8031 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
8034 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
8037 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
8039 channelList_append(&chanserv_conf
.support_channels
, chan
);
8041 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
8042 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
8043 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
8044 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
8045 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
8046 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
8047 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
8048 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
8049 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
8050 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
8051 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
8052 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
8053 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
8054 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
8055 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
8056 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
8057 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
8058 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
8059 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
8060 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
8061 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
8062 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
8063 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
8064 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
8065 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
8067 NickChange(chanserv
, str
, 0);
8068 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
8069 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
8070 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
8071 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
8072 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
8073 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
8074 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
8075 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
8076 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
8077 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
8078 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
8079 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
8080 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
8081 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
8082 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
8083 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
8084 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
8085 god_timeout
= str
? ParseInterval(str
) : 60*15;
8086 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
8089 safestrncpy(mode_line
, str
, sizeof(mode_line
));
8090 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
8091 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
8092 && (change
->argc
< 2))
8094 chanserv_conf
.default_modes
= *change
;
8095 mod_chanmode_free(change
);
8097 free_string_list(chanserv_conf
.set_shows
);
8098 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
8100 strlist
= string_list_copy(strlist
);
8103 static const char *list
[] = {
8104 /* free form text */
8105 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8106 /* options based on user level */
8107 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8108 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
8109 /* multiple choice options */
8110 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
8111 /* binary options */
8112 "DynLimit", "NoDelete", "BanTimeout",
8117 strlist
= alloc_string_list(ArrayLength(list
)-1);
8118 for(ii
=0; list
[ii
]; ii
++)
8119 string_list_append(strlist
, strdup(list
[ii
]));
8121 chanserv_conf
.set_shows
= strlist
;
8122 /* We don't look things up now, in case the list refers to options
8123 * defined by modules initialized after this point. Just mark the
8124 * function list as invalid, so it will be initialized.
8126 set_shows_list
.used
= 0;
8127 free_string_list(chanserv_conf
.eightball
);
8128 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
8131 strlist
= string_list_copy(strlist
);
8135 strlist
= alloc_string_list(4);
8136 string_list_append(strlist
, strdup("Yes."));
8137 string_list_append(strlist
, strdup("No."));
8138 string_list_append(strlist
, strdup("Maybe so."));
8140 chanserv_conf
.eightball
= strlist
;
8141 free_string_list(chanserv_conf
.old_ban_names
);
8142 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
8144 strlist
= string_list_copy(strlist
);
8146 strlist
= alloc_string_list(2);
8147 chanserv_conf
.old_ban_names
= strlist
;
8148 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
8149 off_channel
= str
? atoi(str
) : 0;
8153 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
8156 struct note_type
*ntype
;
8159 if(!(obj
= GET_RECORD_OBJECT(rd
)))
8161 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
8164 if(!(ntype
= chanserv_create_note_type(key
)))
8166 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
8170 /* Figure out set access */
8171 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
8173 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8174 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
8176 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
8178 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
8179 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
8181 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
8183 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
8187 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
8188 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
8189 ntype
->set_access
.min_opserv
= 0;
8192 /* Figure out visibility */
8193 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
8194 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8195 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
8196 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8197 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
8198 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
8199 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
8200 ntype
->visible_type
= NOTE_VIS_ALL
;
8202 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
8204 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
8205 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
8209 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8211 struct handle_info
*handle
;
8212 struct userData
*uData
;
8213 char *seen
, *inf
, *flags
, *expires
, *accessexpiry
, *clvlexpiry
, *lstacc
;
8215 unsigned short access
, lastaccess
= 0;
8217 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8219 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
8223 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
8224 if(access
> UL_OWNER
)
8226 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
8230 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
8231 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
8232 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
8233 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
8234 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8235 accessexpiry
= database_get_data(rd
->d
.object
, KEY_ACCESSEXPIRY
, RECDB_QSTRING
);
8236 clvlexpiry
= database_get_data(rd
->d
.object
, KEY_CLVLEXPIRY
, RECDB_QSTRING
);
8237 lstacc
= database_get_data(rd
->d
.object
, KEY_LASTLEVEL
, RECDB_QSTRING
);
8238 lastaccess
= lstacc
? atoi(lstacc
) : 0;
8240 handle
= get_handle_info(key
);
8243 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
8247 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
, 0);
8248 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
8249 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
8251 uData
->accessexpiry
= accessexpiry
? strtoul(accessexpiry
, NULL
, 0) : 0;
8252 if (uData
->accessexpiry
> 0)
8253 timeq_add(uData
->accessexpiry
, chanserv_expire_tempuser
, uData
);
8255 uData
->clvlexpiry
= clvlexpiry
? strtoul(clvlexpiry
, NULL
, 0) : 0;
8256 if (uData
->clvlexpiry
> 0)
8257 timeq_add(uData
->clvlexpiry
, chanserv_expire_tempclvl
, uData
);
8259 uData
->lastaccess
= lastaccess
;
8261 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
8263 if(uData
->expires
> now
)
8264 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
8266 uData
->flags
&= ~USER_SUSPENDED
;
8269 /* Upgrade: set autoop to the inverse of noautoop */
8270 if(chanserv_read_version
< 2)
8272 /* if noautoop is true, set autoop false, and vice versa */
8273 if(uData
->flags
& USER_NOAUTO_OP
)
8274 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
8276 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
8277 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
);
8283 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
8285 struct banData
*bData
;
8286 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
8287 time_t set_time
, triggered_time
, expires_time
;
8289 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8291 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8295 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8296 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8297 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8298 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8299 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8300 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8301 if (!reason
|| !owner
)
8304 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8305 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8307 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8309 expires_time
= set_time
+ atoi(s_duration
);
8313 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8316 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8319 static struct suspended
*
8320 chanserv_read_suspended(dict_t obj
)
8322 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8326 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8327 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8328 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8329 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8330 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8331 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8332 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8333 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8334 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8335 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8339 static struct giveownership
*
8340 chanserv_read_giveownership(dict_t obj
)
8342 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8346 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8347 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8349 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8351 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8352 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8354 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8355 giveownership
->reason
= str
? strdup(str
) : NULL
;
8356 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8357 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8359 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8360 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8361 return giveownership
;
8365 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8367 struct suspended
*suspended
;
8368 struct giveownership
*giveownership
;
8369 struct mod_chanmode
*modes
;
8370 struct chanNode
*cNode
;
8371 struct chanData
*cData
;
8372 struct dict
*channel
, *obj
;
8373 char *str
, *argv
[10];
8377 channel
= hir
->d
.object
;
8379 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8382 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8385 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8388 cData
= register_channel(cNode
, str
);
8391 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8395 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8397 enum levelOption lvlOpt
;
8398 enum charOption chOpt
;
8400 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8401 cData
->flags
= atoi(str
);
8403 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8405 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8407 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8408 else if(levelOptions
[lvlOpt
].old_flag
)
8410 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8411 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8413 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8417 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8419 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8421 cData
->chOpts
[chOpt
] = str
[0];
8424 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8426 enum levelOption lvlOpt
;
8427 enum charOption chOpt
;
8430 cData
->flags
= base64toint(str
, 5);
8431 count
= strlen(str
+= 5);
8432 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8435 if(levelOptions
[lvlOpt
].old_flag
)
8437 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8438 lvl
= levelOptions
[lvlOpt
].flag_value
;
8440 lvl
= levelOptions
[lvlOpt
].default_value
;
8442 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8444 case 'c': lvl
= UL_COOWNER
; break;
8445 case 'm': lvl
= UL_MANAGER
; break;
8446 case 'n': lvl
= UL_OWNER
+1; break;
8447 case 'o': lvl
= UL_OP
; break;
8448 case 'p': lvl
= UL_PEON
; break;
8449 case 'h': lvl
= UL_HALFOP
; break;
8450 case 'w': lvl
= UL_OWNER
; break;
8451 default: lvl
= 0; break;
8453 cData
->lvlOpts
[lvlOpt
] = lvl
;
8455 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8456 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8459 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8461 suspended
= chanserv_read_suspended(obj
);
8462 cData
->suspended
= suspended
;
8463 suspended
->cData
= cData
;
8464 /* We could use suspended->expires and suspended->revoked to
8465 * set the CHANNEL_SUSPENDED flag, but we don't. */
8467 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8469 suspended
= calloc(1, sizeof(*suspended
));
8470 suspended
->issued
= 0;
8471 suspended
->revoked
= 0;
8472 suspended
->suspender
= strdup(str
);
8473 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8474 suspended
->expires
= str
? atoi(str
) : 0;
8475 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8476 suspended
->reason
= strdup(str
? str
: "No reason");
8477 suspended
->previous
= NULL
;
8478 cData
->suspended
= suspended
;
8479 suspended
->cData
= cData
;
8483 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8484 suspended
= NULL
; /* to squelch a warning */
8487 if(IsSuspended(cData
)) {
8488 if(suspended
->expires
> now
)
8489 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8490 else if(suspended
->expires
)
8491 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8494 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8496 giveownership
= chanserv_read_giveownership(obj
);
8497 cData
->giveownership
= giveownership
;
8500 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8501 struct mod_chanmode change
;
8502 mod_chanmode_init(&change
);
8504 change
.args
[0].mode
= MODE_CHANOP
;
8505 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8506 mod_chanmode_announce(chanserv
, cNode
, &change
);
8509 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8510 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8511 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8512 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8513 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8514 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8515 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8516 cData
->max
= str
? atoi(str
) : 0;
8517 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8518 cData
->greeting
= str
? strdup(str
) : NULL
;
8519 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8520 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8521 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8522 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8523 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8524 cData
->topic
= str
? strdup(str
) : NULL
;
8526 str
= database_get_data(channel
, KEY_MAXSETINFO
, RECDB_QSTRING
);
8527 cData
->maxsetinfo
= str
? strtoul(str
, NULL
, 0) : chanserv_conf
.max_userinfo_length
;
8529 if(!IsSuspended(cData
)
8530 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8531 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8532 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8533 cData
->modes
= *modes
;
8535 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8536 if(cData
->modes
.argc
> 1)
8537 cData
->modes
.argc
= 1;
8538 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8539 mod_chanmode_free(modes
);
8542 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8543 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8544 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8546 if(!cData
->users
&& !IsProtected(cData
))
8548 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8549 unregister_channel(cData
, "has empty user list.");
8553 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8554 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8555 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8557 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8558 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8560 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8561 struct record_data
*rd
= iter_data(it
);
8562 const char *note
, *setter
;
8564 if(rd
->type
!= RECDB_OBJECT
)
8566 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8570 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8572 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8574 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8578 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8579 if(!setter
) setter
= "<unknown>";
8580 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8588 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8590 const char *setter
, *reason
, *str
;
8591 struct do_not_register
*dnr
;
8593 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8596 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8599 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8602 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8605 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8608 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8610 dnr
->set
= atoi(str
);
8616 chanserv_version_read(struct dict
*section
)
8620 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8622 chanserv_read_version
= atoi(str
);
8623 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8627 chanserv_saxdb_read(struct dict
*database
)
8629 struct dict
*section
;
8632 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8633 chanserv_version_read(section
);
8635 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8636 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8637 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8639 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8640 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8641 chanserv_channel_read(iter_key(it
), iter_data(it
));
8643 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8644 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8645 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8651 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8653 int high_present
= 0;
8654 saxdb_start_record(ctx
, KEY_USERS
, 1);
8655 for(; uData
; uData
= uData
->next
)
8657 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8659 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8660 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8661 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8662 saxdb_write_int(ctx
, KEY_ACCESSEXPIRY
, uData
->accessexpiry
);
8663 saxdb_write_int(ctx
, KEY_CLVLEXPIRY
, uData
->clvlexpiry
);
8664 saxdb_write_int(ctx
, KEY_LASTLEVEL
, uData
->lastaccess
);
8666 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8668 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8670 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8671 saxdb_end_record(ctx
);
8673 saxdb_end_record(ctx
);
8674 return high_present
;
8678 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8682 saxdb_start_record(ctx
, KEY_BANS
, 1);
8683 for(; bData
; bData
= bData
->next
)
8685 saxdb_start_record(ctx
, bData
->mask
, 0);
8686 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8687 if(bData
->triggered
)
8688 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8690 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8692 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8694 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8695 saxdb_end_record(ctx
);
8697 saxdb_end_record(ctx
);
8701 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8703 saxdb_start_record(ctx
, name
, 0);
8704 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8705 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8707 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8709 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8711 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8713 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8714 saxdb_end_record(ctx
);
8718 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8720 saxdb_start_record(ctx
, name
, 0);
8721 if(giveownership
->staff_issuer
)
8722 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8723 if(giveownership
->old_owner
)
8724 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8725 if(giveownership
->target
)
8726 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8727 if(giveownership
->target_access
)
8728 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8729 if(giveownership
->reason
)
8730 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8731 if(giveownership
->issued
)
8732 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8733 if(giveownership
->previous
)
8734 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8735 saxdb_end_record(ctx
);
8739 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8743 enum levelOption lvlOpt
;
8744 enum charOption chOpt
;
8746 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8748 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8749 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8751 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8752 if(channel
->registrar
)
8753 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8754 if(channel
->greeting
)
8755 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8756 if(channel
->user_greeting
)
8757 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8758 if(channel
->topic_mask
)
8759 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8760 if(channel
->suspended
)
8761 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8762 if(channel
->giveownership
)
8763 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8765 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8766 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8767 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8768 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8769 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8771 buf
[0] = channel
->chOpts
[chOpt
];
8773 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8775 saxdb_end_record(ctx
);
8777 if (channel
->maxsetinfo
)
8778 saxdb_write_int(ctx
, KEY_MAXSETINFO
, channel
->maxsetinfo
);
8780 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8782 mod_chanmode_format(&channel
->modes
, buf
);
8783 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8786 high_present
= chanserv_write_users(ctx
, channel
->users
);
8787 chanserv_write_bans(ctx
, channel
->bans
);
8789 if(dict_size(channel
->notes
))
8793 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8794 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8796 struct note
*note
= iter_data(it
);
8797 saxdb_start_record(ctx
, iter_key(it
), 0);
8798 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8799 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8800 saxdb_end_record(ctx
);
8802 saxdb_end_record(ctx
);
8805 if(channel
->ownerTransfer
)
8806 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8807 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8808 saxdb_end_record(ctx
);
8812 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8816 saxdb_start_record(ctx
, ntype
->name
, 0);
8817 switch(ntype
->set_access_type
)
8819 case NOTE_SET_CHANNEL_ACCESS
:
8820 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8822 case NOTE_SET_CHANNEL_SETTER
:
8823 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8825 case NOTE_SET_PRIVILEGED
: default:
8826 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8829 switch(ntype
->visible_type
)
8831 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8832 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8833 case NOTE_VIS_PRIVILEGED
:
8834 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8836 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8837 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8838 saxdb_end_record(ctx
);
8842 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8844 struct do_not_register
*dnr
;
8847 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8849 dnr
= iter_data(it
);
8850 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8852 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8853 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8854 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8855 saxdb_end_record(ctx
);
8860 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8863 struct chanData
*channel
;
8865 /* Version Control*/
8866 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8867 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8868 saxdb_end_record(ctx
);
8871 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8872 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8873 chanserv_write_note_type(ctx
, iter_data(it
));
8874 saxdb_end_record(ctx
);
8877 saxdb_start_record(ctx
, KEY_DNR
, 1);
8878 write_dnrs_helper(ctx
, handle_dnrs
);
8879 write_dnrs_helper(ctx
, plain_dnrs
);
8880 write_dnrs_helper(ctx
, mask_dnrs
);
8881 saxdb_end_record(ctx
);
8884 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8885 for(channel
= channelList
; channel
; channel
= channel
->next
)
8886 chanserv_write_channel(ctx
, channel
);
8887 saxdb_end_record(ctx
);
8893 chanserv_db_cleanup(void) {
8895 unreg_part_func(handle_part
);
8897 unregister_channel(channelList
, "terminating.");
8898 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8899 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8900 free(chanserv_conf
.support_channels
.list
);
8901 dict_delete(handle_dnrs
);
8902 dict_delete(plain_dnrs
);
8903 dict_delete(mask_dnrs
);
8904 dict_delete(note_types
);
8905 free_string_list(chanserv_conf
.eightball
);
8906 free_string_list(chanserv_conf
.old_ban_names
);
8907 free_string_list(chanserv_conf
.set_shows
);
8908 free(set_shows_list
.list
);
8909 free(uset_shows_list
.list
);
8912 struct userData
*helper
= helperList
;
8913 helperList
= helperList
->next
;
8918 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8919 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8920 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8923 init_chanserv(const char *nick
)
8925 struct chanNode
*chan
;
8927 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8928 conf_register_reload(chanserv_conf_read
);
8930 reg_server_link_func(handle_server_link
);
8932 reg_new_channel_func(handle_new_channel
);
8933 reg_join_func(handle_join
);
8934 reg_part_func(handle_part
);
8935 reg_kick_func(handle_kick
);
8936 reg_topic_func(handle_topic
);
8937 reg_mode_change_func(handle_mode
);
8938 reg_nick_change_func(handle_nick_change
);
8940 reg_auth_func(handle_auth
);
8941 reg_handle_rename_func(handle_rename
);
8942 reg_unreg_func(handle_unreg
);
8944 handle_dnrs
= dict_new();
8945 dict_set_free_data(handle_dnrs
, free
);
8946 plain_dnrs
= dict_new();
8947 dict_set_free_data(plain_dnrs
, free
);
8948 mask_dnrs
= dict_new();
8949 dict_set_free_data(mask_dnrs
, free
);
8951 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8952 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8953 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8954 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8955 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8956 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8957 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8958 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8959 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8960 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8962 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8964 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8965 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8967 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8968 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8969 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8970 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8971 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8973 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8974 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8975 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8976 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8977 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8978 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8980 DEFINE_COMMAND(levels
, 1, 0, NULL
);
8982 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8983 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8984 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8985 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8987 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8988 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8989 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8990 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8991 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8992 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8993 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8994 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8995 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8996 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8998 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8999 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9000 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
9001 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
9002 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
9003 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
9004 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
9005 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
9006 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
9007 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
9008 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
9009 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
9010 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9011 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
9013 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9014 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
9015 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9016 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9018 /* if you change dellamer access, see also places
9019 * like unbanme which have manager hardcoded. */
9020 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
9021 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
9023 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
9025 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
9027 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
9028 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9029 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9030 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9031 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9032 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9033 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9034 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9035 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9036 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9037 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9038 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
9040 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
9041 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
9043 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
9044 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
9045 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
9046 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
9048 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9049 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
9050 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
9051 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
9052 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
9054 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9055 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9056 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9057 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9058 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9059 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9060 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9061 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
9063 /* Channel options */
9064 DEFINE_CHANNEL_OPTION(defaulttopic
);
9065 DEFINE_CHANNEL_OPTION(topicmask
);
9066 DEFINE_CHANNEL_OPTION(greeting
);
9067 DEFINE_CHANNEL_OPTION(usergreeting
);
9068 DEFINE_CHANNEL_OPTION(modes
);
9069 DEFINE_CHANNEL_OPTION(enfops
);
9070 DEFINE_CHANNEL_OPTION(enfhalfops
);
9071 DEFINE_CHANNEL_OPTION(automode
);
9072 DEFINE_CHANNEL_OPTION(protect
);
9073 DEFINE_CHANNEL_OPTION(enfmodes
);
9074 DEFINE_CHANNEL_OPTION(enftopic
);
9075 DEFINE_CHANNEL_OPTION(pubcmd
);
9076 DEFINE_CHANNEL_OPTION(userinfo
);
9077 DEFINE_CHANNEL_OPTION(dynlimit
);
9078 DEFINE_CHANNEL_OPTION(topicsnarf
);
9079 DEFINE_CHANNEL_OPTION(nodelete
);
9080 DEFINE_CHANNEL_OPTION(toys
);
9081 DEFINE_CHANNEL_OPTION(setters
);
9082 DEFINE_CHANNEL_OPTION(topicrefresh
);
9083 DEFINE_CHANNEL_OPTION(resync
);
9084 DEFINE_CHANNEL_OPTION(ctcpreaction
);
9085 DEFINE_CHANNEL_OPTION(bantimeout
);
9086 DEFINE_CHANNEL_OPTION(inviteme
);
9087 DEFINE_CHANNEL_OPTION(maxsetinfo
);
9089 DEFINE_CHANNEL_OPTION(offchannel
);
9090 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
9092 /* Alias set topic to set defaulttopic for compatibility. */
9093 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
9096 DEFINE_USER_OPTION(autoinvite
);
9097 DEFINE_USER_OPTION(autojoin
);
9098 DEFINE_USER_OPTION(info
);
9099 DEFINE_USER_OPTION(autoop
);
9101 /* Alias uset autovoice to uset autoop. */
9102 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
9104 note_types
= dict_new();
9105 dict_set_free_data(note_types
, chanserv_deref_note_type
);
9108 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
9109 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
9110 service_register(chanserv
)->trigger
= '!';
9111 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
9114 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
9116 if(chanserv_conf
.channel_expire_frequency
)
9117 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
9119 if(chanserv_conf
.ban_timeout_frequency
)
9120 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
9122 if(chanserv_conf
.refresh_period
)
9124 time_t next_refresh
;
9125 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
9126 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
9127 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
9130 if (autojoin_channels
&& chanserv
) {
9131 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
9132 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
9133 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
9137 reg_exit_func(chanserv_db_cleanup
);
9138 message_register_table(msgtab
);