1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_VERSION_CONTROL "version_control"
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
65 /* version control paramiter */
66 #define KEY_VERSION_NUMBER "version_number"
68 /* Note type parameters */
69 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
70 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
71 #define KEY_NOTE_SETTER_ACCESS "setter_access"
72 #define KEY_NOTE_VISIBILITY "visibility"
73 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
74 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
75 #define KEY_NOTE_VIS_ALL "all"
76 #define KEY_NOTE_MAX_LENGTH "max_length"
77 #define KEY_NOTE_SETTER "setter"
78 #define KEY_NOTE_NOTE "note"
80 /* Do-not-register channels */
82 #define KEY_DNR_SET "set"
83 #define KEY_DNR_SETTER "setter"
84 #define KEY_DNR_REASON "reason"
87 #define KEY_REGISTERED "registered"
88 #define KEY_REGISTRAR "registrar"
89 #define KEY_SUSPENDED "suspended"
90 #define KEY_PREVIOUS "previous"
91 #define KEY_SUSPENDER "suspender"
92 #define KEY_ISSUED "issued"
93 #define KEY_REVOKED "revoked"
94 #define KEY_SUSPEND_EXPIRES "suspend_expires"
95 #define KEY_SUSPEND_REASON "suspend_reason"
96 #define KEY_GIVEOWNERSHIP "giveownership"
97 #define KEY_STAFF_ISSUER "staff_issuer"
98 #define KEY_OLD_OWNER "old_owner"
99 #define KEY_TARGET "target"
100 #define KEY_TARGET_ACCESS "target_access"
101 #define KEY_VISITED "visited"
102 #define KEY_TOPIC "topic"
103 #define KEY_GREETING "greeting"
104 #define KEY_USER_GREETING "user_greeting"
105 #define KEY_MODES "modes"
106 #define KEY_FLAGS "flags"
107 #define KEY_OPTIONS "options"
108 #define KEY_USERS "users"
109 #define KEY_BANS "bans" /* for lamers */
110 #define KEY_MAX "max"
111 #define KEY_NOTES "notes"
112 #define KEY_TOPIC_MASK "topic_mask"
113 #define KEY_OWNER_TRANSFER "owner_transfer"
116 #define KEY_LEVEL "level"
117 #define KEY_INFO "info"
118 #define KEY_SEEN "seen"
121 #define KEY_OWNER "owner"
122 #define KEY_REASON "reason"
123 #define KEY_SET "set"
124 #define KEY_DURATION "duration"
125 #define KEY_EXPIRES "expires"
126 #define KEY_TRIGGERED "triggered"
128 #define KEY_GOD_TIMEOUT "god_timeout"
130 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
131 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
133 /* Administrative messages */
134 static const struct message_entry msgtab
[] = {
135 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
137 /* Channel registration */
138 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
139 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
140 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
141 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
142 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
143 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
144 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
145 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
147 /* Do-not-register channels */
148 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
150 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
151 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
152 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
153 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
154 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
155 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
156 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
157 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
158 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
160 /* Channel unregistration */
161 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
162 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
163 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
164 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
167 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
168 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
170 /* Channel merging */
171 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
172 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
173 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
174 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
175 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
177 /* Handle unregistration */
178 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
181 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
182 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
183 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
184 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
185 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
186 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
187 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
188 { "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." },
189 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
190 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
191 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
192 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
193 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
194 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
196 /* Removing yourself from a channel. */
197 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
198 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
199 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
201 /* User management */
202 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
203 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
204 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
205 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
206 { "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." },
207 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
208 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
209 { "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." },
210 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
211 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
212 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
213 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
214 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
215 { "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" },
216 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
218 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
219 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
220 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
221 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
222 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
223 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
226 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
227 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
228 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
229 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
230 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
231 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
232 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
233 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
234 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
235 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
236 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
237 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
238 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
239 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
240 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
241 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
242 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
244 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
246 /* Channel management */
247 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
248 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
249 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
251 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
252 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
253 { "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" },
254 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
255 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
256 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
257 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
259 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
260 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
261 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
262 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
263 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
264 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
265 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
266 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
267 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
280 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
281 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
282 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
283 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
284 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
285 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
286 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
287 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
288 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
289 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
290 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
291 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
292 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
293 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
294 { "CSMSG_SET_BANTYPE", "$bBanType $b %d - %s" },
295 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
297 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
298 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
299 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
300 { "CSMSG_USET_INFO", "$bInfo $b %s" },
302 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
303 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
304 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
305 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
306 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
307 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
308 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
309 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
310 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
311 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
312 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
314 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
315 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
316 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
317 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
318 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
319 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
321 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
322 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
323 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
324 { "CSMSG_PROTECT_NONE", "No users will be protected." },
325 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
326 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
327 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
329 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
330 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
331 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
332 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
333 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
335 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
336 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
337 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
338 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
339 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
341 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
342 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
343 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
344 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
345 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
347 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
348 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
349 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
350 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
351 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
352 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
354 { "CSMSG_BANTYPE_A", "*!user@host" },
355 { "CSMSG_BANTYPE_B", "*!*user@host" },
356 { "CSMSG_BANTYPE_C", "*!*@host" },
357 { "CSMSG_BANTYPE_D", "*!*user@*.host" },
358 { "CSMSG_BANTYPE_E", "*!*@*.host" },
359 { "CSMSG_BANTYPE_F", "nick!user@host" },
360 { "CSMSG_BANTYPE_G", "nick!*@host" },
361 { "CSMSG_BANTYPE_H", "nick!*user@*.host" },
362 { "CSMSG_BANTYPE_I", "nick!*@*.host" },
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_ALREADY_PRESENT", "%s is already in $b%s$b." },
368 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
369 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
370 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
371 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
373 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
374 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
375 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
377 /* Channel userlist */
378 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
379 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
380 /* uncomment if needed to adujust styles (and change code below)
381 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
382 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
383 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
384 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
385 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
386 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
388 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
389 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
390 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
392 /* Channel note list */
393 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
394 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
395 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
396 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
397 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
398 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
399 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
400 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
401 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
402 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
403 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
404 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
405 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
406 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
407 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
409 /* Channel [un]suspension */
410 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
411 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
412 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
413 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
414 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
415 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
416 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
418 /* Access information */
419 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
420 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
421 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
422 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
423 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
424 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
425 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
426 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
427 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
428 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
429 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
431 /* Seen information */
432 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
433 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
434 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
435 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
437 /* Names information */
438 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
439 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
441 /* Channel information */
442 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
443 { "CSMSG_BAR", "----------------------------------------"},
444 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
445 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
446 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
447 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
448 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
449 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
450 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
451 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
452 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
453 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
454 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
455 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
456 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
457 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
458 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
459 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
460 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
461 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
462 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
463 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
464 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
465 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
466 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
467 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
468 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
469 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
471 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
472 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
473 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
474 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
475 { "CSMSG_PEEK_OPS", "$bOps:$b" },
476 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
477 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
479 /* Network information */
480 { "CSMSG_NETWORK_INFO", "Network Information:" },
481 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
482 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
483 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
484 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
485 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
486 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
487 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
488 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
491 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
492 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
493 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
495 /* Channel searches */
496 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
497 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
498 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
499 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
501 /* Channel configuration */
502 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
503 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
504 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
505 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
508 { "CSMSG_USER_OPTIONS", "User Options:" },
509 // { "CSMSG_USER_PROTECTED", "That user is protected." },
512 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
513 { "CSMSG_PING_RESPONSE", "Pong!" },
514 { "CSMSG_WUT_RESPONSE", "wut" },
515 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
516 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
517 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
518 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
519 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
520 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
521 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
524 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
525 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
529 /* eject_user and unban_user flags */
530 #define ACTION_KICK 0x0001
531 #define ACTION_BAN 0x0002
532 #define ACTION_ADD_LAMER 0x0004
533 #define ACTION_ADD_TIMED_LAMER 0x0008
534 #define ACTION_UNBAN 0x0010
535 #define ACTION_DEL_LAMER 0x0020
537 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
538 #define MODELEN 40 + KEYLEN
542 #define CSFUNC_ARGS user, channel, argc, argv, cmd
544 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
545 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
546 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
547 reply("MSG_MISSING_PARAMS", argv[0]); \
551 DECLARE_LIST(dnrList
, struct do_not_register
*);
552 DEFINE_LIST(dnrList
, struct do_not_register
*);
554 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
556 struct userNode
*chanserv
;
559 extern struct string_list
*autojoin_channels
;
560 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
561 static struct log_type
*CS_LOG
;
562 struct adduserPending
* adduser_pendings
= NULL
;
563 extern const char *hidden_host_suffix
;
564 unsigned int adduser_pendings_count
= 0;
565 unsigned long god_timeout
;
569 struct channelList support_channels
;
570 struct mod_chanmode default_modes
;
572 unsigned long db_backup_frequency
;
573 unsigned long channel_expire_frequency
;
574 unsigned long ban_timeout_frequency
;
577 unsigned int adjust_delay
;
578 long channel_expire_delay
;
579 unsigned int nodelete_level
;
581 unsigned int adjust_threshold
;
582 int join_flood_threshold
;
584 unsigned int greeting_length
;
585 unsigned int refresh_period
;
586 unsigned int giveownership_period
;
588 unsigned int max_owned
;
589 unsigned int max_chan_users
;
590 unsigned int max_chan_bans
; /* lamers */
591 unsigned int max_userinfo_length
;
593 struct string_list
*set_shows
;
594 struct string_list
*eightball
;
595 struct string_list
*old_ban_names
;
597 const char *ctcp_short_ban_duration
;
598 const char *ctcp_long_ban_duration
;
600 const char *irc_operator_epithet
;
601 const char *network_helper_epithet
;
602 const char *support_helper_epithet
;
607 struct userNode
*user
;
608 struct userNode
*bot
;
609 struct chanNode
*channel
;
611 unsigned short lowest
;
612 unsigned short highest
;
613 struct userData
**users
;
614 struct helpfile_table table
;
617 enum note_access_type
619 NOTE_SET_CHANNEL_ACCESS
,
620 NOTE_SET_CHANNEL_SETTER
,
624 enum note_visible_type
627 NOTE_VIS_CHANNEL_USERS
,
633 enum note_access_type set_access_type
;
635 unsigned int min_opserv
;
636 unsigned short min_ulevel
;
638 enum note_visible_type visible_type
;
639 unsigned int max_length
;
646 struct note_type
*type
;
647 char setter
[NICKSERV_HANDLE_LEN
+1];
651 static unsigned int registered_channels
;
652 static unsigned int banCount
;
654 static const struct {
657 unsigned short level
;
659 } accessLevels
[] = { /* MUST be orderd less to most! */
660 { "peon", "Peon", UL_PEON
, '+' },
661 { "halfop", "HalfOp", UL_HALFOP
, '%' },
662 { "op", "Op", UL_OP
, '@' },
663 { "manager", "Manager", UL_MANAGER
, '%' },
664 { "coowner", "Coowner", UL_COOWNER
, '*' },
665 { "owner", "Owner", UL_OWNER
, '!' },
666 { "helper", "BUG:", UL_HELPER
, 'X' }
669 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
670 static const struct {
673 unsigned short default_value
;
674 unsigned int old_idx
;
675 unsigned int old_flag
;
676 unsigned short flag_value
;
678 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
679 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
680 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
681 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
682 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
683 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
684 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
685 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
686 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
689 struct charOptionValues
{
692 } automodeValues
[] = {
693 { 'n', "CSMSG_AUTOMODE_NONE" },
694 { 'y', "CSMSG_AUTOMODE_NORMAL" },
695 { 'v', "CSMSG_AUTOMODE_VOICE" },
696 { 'h', "CSMSG_AUTOMODE_HOP" },
697 { 'o', "CSMSG_AUTOMODE_OP" },
698 { 'm', "CSMSG_AUTOMODE_MUTE" }
699 }, protectValues
[] = {
700 { 'a', "CSMSG_PROTECT_ALL" },
701 { 'e', "CSMSG_PROTECT_EQUAL" },
702 { 'l', "CSMSG_PROTECT_LOWER" },
703 { 'n', "CSMSG_PROTECT_NONE" }
705 { 'd', "CSMSG_TOYS_DISABLED" },
706 { 'n', "CSMSG_TOYS_PRIVATE" },
707 { 'p', "CSMSG_TOYS_PUBLIC" }
708 }, topicRefreshValues
[] = {
709 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
710 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
711 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
712 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
713 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
714 }, ctcpReactionValues
[] = {
715 { 'n', "CSMSG_CTCPREACTION_NONE" },
716 { 'k', "CSMSG_CTCPREACTION_KICK" },
717 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
718 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
719 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
720 }, banTimeoutValues
[] = {
721 { '0', "CSMSG_BANTIMEOUT_NONE" },
722 { '1', "CSMSG_BANTIMEOUT_10M" },
723 { '2', "CSMSG_BANTIMEOUT_2H" },
724 { '3', "CSMSG_BANTIMEOUT_4H" },
725 { '4', "CSMSG_BANTIMEOUT_1D" },
726 { '5', "CSMSG_BANTIMEOUT_1W" }
727 }, resyncValues
[] = {
728 { 'n', "CSMSG_RESYNC_NEVER" },
729 { '1', "CSMSG_RESYNC_3_HOURS" },
730 { '2', "CSMSG_RESYNC_6_HOURS" },
731 { '3', "CSMSG_RESYNC_12_HOURS" },
732 { '4', "CSMSG_RESYNC_24_HOURS" }
733 }, banTypeValues
[] = {
734 { '1', "CSMSG_BANTYPE_A" },
735 { '2', "CSMSG_BANTYPE_B" },
736 { '3', "CSMSG_BANTYPE_C" },
737 { '4', "CSMSG_BANTYPE_D" },
738 { '5', "CSMSG_BANTYPE_E" },
739 { '6', "CSMSG_BANTYPE_F" },
740 { '7', "CSMSG_BANTYPE_G" },
741 { '8', "CSMSG_BANTYPE_H" },
742 { '9', "CSMSG_BANTYPE_I" }
745 static const struct {
749 unsigned int old_idx
;
751 struct charOptionValues
*values
;
753 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
754 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
755 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
756 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
757 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
758 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
759 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
760 { "CSMSG_SET_BANTYPE", "bantype", '4', 13, ArrayLength(banTypeValues
), banTypeValues
},
763 struct userData
*helperList
;
764 struct chanData
*channelList
;
765 static struct module *chanserv_module
;
766 static unsigned int userCount
;
767 unsigned int chanserv_read_version
= 0; /* db version control */
769 #define CHANSERV_DB_VERSION 2
771 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
772 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
775 user_level_from_name(const char *name
, unsigned short clamp_level
)
777 unsigned int level
= 0, ii
;
779 level
= strtoul(name
, NULL
, 10);
780 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
781 if(!irccasecmp(name
, accessLevels
[ii
].name
))
782 level
= accessLevels
[ii
].level
;
783 if(level
> clamp_level
)
789 user_level_name_from_level(int level
)
797 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
798 if(level
>= accessLevels
[ii
].level
)
799 highest
= accessLevels
[ii
].title
;
805 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
808 *minl
= strtoul(arg
, &sep
, 10);
816 *maxl
= strtoul(sep
+1, &sep
, 10);
824 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
826 struct userData
*uData
, **head
;
828 if(!channel
|| !handle
)
831 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
832 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
834 for(uData
= helperList
;
835 uData
&& uData
->handle
!= handle
;
836 uData
= uData
->next
);
840 uData
= calloc(1, sizeof(struct userData
));
841 uData
->handle
= handle
;
843 uData
->access
= UL_HELPER
;
849 uData
->next
= helperList
;
851 helperList
->prev
= uData
;
859 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
860 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
863 head
= &(channel
->users
);
866 if(uData
&& (uData
!= *head
))
868 /* Shuffle the user to the head of whatever list he was in. */
870 uData
->next
->prev
= uData
->prev
;
872 uData
->prev
->next
= uData
->next
;
878 (**head
).prev
= uData
;
885 /* Returns non-zero if user has at least the minimum access.
886 * exempt_owner is set when handling !set, so the owner can set things
889 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
891 struct userData
*uData
;
892 struct chanData
*cData
= channel
->channel_info
;
893 unsigned short minimum
= cData
->lvlOpts
[opt
];
896 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
899 if(minimum
<= uData
->access
)
901 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
906 /* Scan for other users authenticated to the same handle
907 still in the channel. If so, keep them listed as present.
909 user is optional, if not null, it skips checking that userNode
910 (for the handle_part function) */
912 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
916 if(IsSuspended(uData
->channel
)
917 || IsUserSuspended(uData
)
918 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
930 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
932 unsigned int eflags
, argc
;
934 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
936 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
937 if(!channel
->channel_info
938 || IsSuspended(channel
->channel_info
)
940 || !ircncasecmp(text
, "ACTION ", 7))
942 /* We dont punish people we know -Rubin
943 * * Figure out the minimum level needed to CTCP the channel *
945 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
948 /* If they are a user of the channel, they are exempt */
949 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
951 /* We need to enforce against them; do so. */
954 argv
[1] = user
->nick
;
956 if(GetUserMode(channel
, user
))
957 eflags
|= ACTION_KICK
;
958 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
959 default: case 'n': return;
961 eflags
|= ACTION_KICK
;
964 eflags
|= ACTION_BAN
;
967 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
968 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
971 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
972 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
975 argv
[argc
++] = bad_ctcp_reason
;
976 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
980 chanserv_create_note_type(const char *name
)
982 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
983 strcpy(ntype
->name
, name
);
985 dict_insert(note_types
, ntype
->name
, ntype
);
990 chanserv_deref_note_type(void *data
)
992 struct note_type
*ntype
= data
;
994 if(--ntype
->refs
> 0)
1000 chanserv_flush_note_type(struct note_type
*ntype
)
1002 struct chanData
*cData
;
1003 for(cData
= channelList
; cData
; cData
= cData
->next
)
1004 dict_remove(cData
->notes
, ntype
->name
);
1008 chanserv_truncate_notes(struct note_type
*ntype
)
1010 struct chanData
*cData
;
1012 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
1014 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1015 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1018 if(strlen(note
->note
) <= ntype
->max_length
)
1020 dict_remove2(cData
->notes
, ntype
->name
, 1);
1021 note
= realloc(note
, size
);
1022 note
->note
[ntype
->max_length
] = 0;
1023 dict_insert(cData
->notes
, ntype
->name
, note
);
1027 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1029 static struct note
*
1030 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1033 unsigned int len
= strlen(text
);
1035 if(len
> type
->max_length
) len
= type
->max_length
;
1036 note
= calloc(1, sizeof(*note
) + len
);
1038 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1039 memcpy(note
->note
, text
, len
);
1040 note
->note
[len
] = 0;
1041 dict_insert(channel
->notes
, type
->name
, note
);
1047 chanserv_free_note(void *data
)
1049 struct note
*note
= data
;
1051 chanserv_deref_note_type(note
->type
);
1052 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1056 static MODCMD_FUNC(cmd_createnote
) {
1057 struct note_type
*ntype
;
1058 unsigned int arg
= 1, existed
= 0, max_length
;
1060 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1063 ntype
= chanserv_create_note_type(argv
[arg
]);
1064 if(!irccasecmp(argv
[++arg
], "privileged"))
1067 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1068 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1070 else if(!irccasecmp(argv
[arg
], "channel"))
1072 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1075 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1078 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1079 ntype
->set_access
.min_ulevel
= ulvl
;
1081 else if(!irccasecmp(argv
[arg
], "setter"))
1083 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1087 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1091 if(!irccasecmp(argv
[++arg
], "privileged"))
1092 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1093 else if(!irccasecmp(argv
[arg
], "channel_users"))
1094 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1095 else if(!irccasecmp(argv
[arg
], "all"))
1096 ntype
->visible_type
= NOTE_VIS_ALL
;
1098 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1102 if((arg
+1) >= argc
) {
1103 reply("MSG_MISSING_PARAMS", argv
[0]);
1106 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1107 if(max_length
< 20 || max_length
> 450)
1109 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1112 if(existed
&& (max_length
< ntype
->max_length
))
1114 ntype
->max_length
= max_length
;
1115 chanserv_truncate_notes(ntype
);
1117 ntype
->max_length
= max_length
;
1120 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1122 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1127 dict_remove(note_types
, ntype
->name
);
1131 static MODCMD_FUNC(cmd_removenote
) {
1132 struct note_type
*ntype
;
1135 ntype
= dict_find(note_types
, argv
[1], NULL
);
1136 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1139 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1146 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1149 chanserv_flush_note_type(ntype
);
1151 dict_remove(note_types
, argv
[1]);
1152 reply("CSMSG_NOTE_DELETED", argv
[1]);
1157 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1161 if(orig
->modes_set
& change
->modes_clear
)
1163 if(orig
->modes_clear
& change
->modes_set
)
1165 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1166 && strcmp(orig
->new_key
, change
->new_key
))
1168 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1169 && (orig
->new_limit
!= change
->new_limit
))
1174 static char max_length_text
[MAXLEN
+1][16];
1176 static struct helpfile_expansion
1177 chanserv_expand_variable(const char *variable
)
1179 struct helpfile_expansion exp
;
1181 if(!irccasecmp(variable
, "notes"))
1184 exp
.type
= HF_TABLE
;
1185 exp
.value
.table
.length
= 1;
1186 exp
.value
.table
.width
= 3;
1187 exp
.value
.table
.flags
= 0;
1188 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1189 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1190 exp
.value
.table
.contents
[0][0] = "Note Type";
1191 exp
.value
.table
.contents
[0][1] = "Visibility";
1192 exp
.value
.table
.contents
[0][2] = "Max Length";
1193 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1195 struct note_type
*ntype
= iter_data(it
);
1198 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1199 row
= exp
.value
.table
.length
++;
1200 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1201 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1202 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1203 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1205 if(!max_length_text
[ntype
->max_length
][0])
1206 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1207 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1212 exp
.type
= HF_STRING
;
1213 exp
.value
.str
= NULL
;
1217 static struct chanData
*
1218 register_channel(struct chanNode
*cNode
, char *registrar
)
1220 struct chanData
*channel
;
1221 enum levelOption lvlOpt
;
1222 enum charOption chOpt
;
1224 channel
= calloc(1, sizeof(struct chanData
));
1226 channel
->notes
= dict_new();
1227 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1229 channel
->registrar
= strdup(registrar
);
1230 channel
->registered
= now
;
1231 channel
->visited
= now
;
1232 channel
->limitAdjusted
= now
;
1233 channel
->ownerTransfer
= now
;
1234 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1235 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1236 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1237 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1238 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1240 channel
->prev
= NULL
;
1241 channel
->next
= channelList
;
1244 channelList
->prev
= channel
;
1245 channelList
= channel
;
1246 registered_channels
++;
1248 channel
->channel
= cNode
;
1250 cNode
->channel_info
= channel
;
1255 static struct userData
*
1256 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1258 struct userData
*ud
;
1260 if(access
> UL_OWNER
)
1263 ud
= calloc(1, sizeof(*ud
));
1264 ud
->channel
= channel
;
1265 ud
->handle
= handle
;
1267 ud
->access
= access
;
1268 ud
->info
= info
? strdup(info
) : NULL
;
1271 ud
->next
= channel
->users
;
1273 channel
->users
->prev
= ud
;
1274 channel
->users
= ud
;
1276 channel
->userCount
++;
1280 ud
->u_next
= ud
->handle
->channels
;
1282 ud
->u_next
->u_prev
= ud
;
1283 ud
->handle
->channels
= ud
;
1285 ud
->flags
= USER_FLAGS_DEFAULT
;
1289 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1292 del_channel_user(struct userData
*user
, int do_gc
)
1294 struct chanData
*channel
= user
->channel
;
1296 channel
->userCount
--;
1300 user
->prev
->next
= user
->next
;
1302 channel
->users
= user
->next
;
1304 user
->next
->prev
= user
->prev
;
1307 user
->u_prev
->u_next
= user
->u_next
;
1309 user
->handle
->channels
= user
->u_next
;
1311 user
->u_next
->u_prev
= user
->u_prev
;
1315 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1316 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1317 unregister_channel(channel
, "lost all users.");
1321 static struct adduserPending
*
1322 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1324 struct adduserPending
*ap
;
1325 ap
= calloc(1,sizeof(struct adduserPending
));
1326 ap
->channel
= channel
;
1329 ap
->created
= time(NULL
);
1331 /* ap->prev defaults to NULL already.. */
1332 ap
->next
= adduser_pendings
;
1333 if(adduser_pendings
)
1334 adduser_pendings
->prev
= ap
;
1335 adduser_pendings
= ap
;
1336 adduser_pendings_count
++;
1341 del_adduser_pending(struct adduserPending
*ap
)
1344 ap
->prev
->next
= ap
->next
;
1346 adduser_pendings
= ap
->next
;
1349 ap
->next
->prev
= ap
->prev
;
1353 static void expire_adduser_pending();
1355 /* find_adduser_pending(channel, user) will find an arbitrary record
1356 * from user, channel, or user and channel.
1357 * if user or channel are NULL, they will match any records.
1359 static struct adduserPending
*
1360 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1362 struct adduserPending
*ap
;
1364 expire_adduser_pending(); /* why not here.. */
1366 if(!channel
&& !user
) /* 2 nulls matches all */
1367 return(adduser_pendings
);
1368 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1370 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1377 /* Remove all pendings for a user or channel
1379 * called in nickserv.c DelUser() and proto-* unregister_channel()
1382 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1384 struct adduserPending
*ap
;
1386 /* So this is a bit wastefull, i hate dealing with linked lists.
1387 * if its a problem we'll rewrite it right */
1388 while((ap
= find_adduser_pending(channel
, user
))) {
1389 del_adduser_pending(ap
);
1393 /* Called from nickserv.c cmd_auth after someone auths */
1395 process_adduser_pending(struct userNode
*user
)
1397 struct adduserPending
*ap
;
1398 if(!user
->handle_info
)
1399 return; /* not associated with an account */
1400 while((ap
= find_adduser_pending(NULL
, user
)))
1402 struct userData
*actee
;
1403 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1405 /* Already on the userlist. do nothing*/
1409 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1410 scan_user_presence(actee
, NULL
);
1412 del_adduser_pending(ap
);
1417 expire_adduser_pending()
1419 struct adduserPending
*ap
, *ap_next
;
1420 ap
= adduser_pendings
;
1423 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1425 ap_next
= ap
->next
; /* save next */
1426 del_adduser_pending(ap
); /* free and relink */
1427 ap
= ap_next
; /* advance */
1434 static void expire_ban(void *data
);
1437 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1440 unsigned int ii
, l1
, l2
;
1445 bd
= malloc(sizeof(struct banData
));
1447 bd
->channel
= channel
;
1449 bd
->triggered
= triggered
;
1450 bd
->expires
= expires
;
1452 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1454 extern const char *hidden_host_suffix
;
1455 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1459 l2
= strlen(old_name
);
1462 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1464 new_mask
= alloca(MAXLEN
);
1465 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1468 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1470 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1471 bd
->reason
= strdup(reason
);
1474 timeq_add(expires
, expire_ban
, bd
);
1477 bd
->next
= channel
->bans
; /* lamers */
1479 channel
->bans
->prev
= bd
;
1481 channel
->banCount
++;
1488 del_channel_ban(struct banData
*ban
)
1490 ban
->channel
->banCount
--;
1494 ban
->prev
->next
= ban
->next
;
1496 ban
->channel
->bans
= ban
->next
;
1499 ban
->next
->prev
= ban
->prev
;
1502 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1511 expire_ban(void *data
) /* lamer.. */
1513 struct banData
*bd
= data
;
1514 if(!IsSuspended(bd
->channel
))
1516 struct banList bans
;
1517 struct mod_chanmode change
;
1519 bans
= bd
->channel
->channel
->banlist
;
1520 mod_chanmode_init(&change
);
1521 for(ii
=0; ii
<bans
.used
; ii
++)
1523 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1526 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1527 change
.args
[0].u
.hostmask
= bd
->mask
;
1528 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1534 del_channel_ban(bd
);
1537 static void chanserv_expire_suspension(void *data
);
1540 unregister_channel(struct chanData
*channel
, const char *reason
)
1542 struct mod_chanmode change
;
1543 char msgbuf
[MAXLEN
];
1545 /* After channel unregistration, the following must be cleaned
1547 - Channel information.
1549 - Channel bans. (lamers)
1550 - Channel suspension data.
1551 - adduser_pending data.
1552 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1558 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1562 mod_chanmode_init(&change
);
1563 change
.modes_clear
|= MODE_REGISTERED
;
1564 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1567 wipe_adduser_pending(channel
->channel
, NULL
);
1569 while(channel
->users
)
1570 del_channel_user(channel
->users
, 0);
1572 while(channel
->bans
)
1573 del_channel_ban(channel
->bans
);
1575 free(channel
->topic
);
1576 free(channel
->registrar
);
1577 free(channel
->greeting
);
1578 free(channel
->user_greeting
);
1579 free(channel
->topic_mask
);
1582 channel
->prev
->next
= channel
->next
;
1584 channelList
= channel
->next
;
1587 channel
->next
->prev
= channel
->prev
;
1589 if(channel
->suspended
)
1591 struct chanNode
*cNode
= channel
->channel
;
1592 struct suspended
*suspended
, *next_suspended
;
1594 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1596 next_suspended
= suspended
->previous
;
1597 free(suspended
->suspender
);
1598 free(suspended
->reason
);
1599 if(suspended
->expires
)
1600 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1605 cNode
->channel_info
= NULL
;
1607 channel
->channel
->channel_info
= NULL
;
1609 dict_delete(channel
->notes
);
1610 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1611 if(!IsSuspended(channel
))
1612 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1613 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1614 UnlockChannel(channel
->channel
);
1616 registered_channels
--;
1620 expire_channels(UNUSED_ARG(void *data
))
1622 struct chanData
*channel
, *next
;
1623 struct userData
*user
;
1624 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1626 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1627 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1629 for(channel
= channelList
; channel
; channel
= next
)
1631 next
= channel
->next
;
1633 /* See if the channel can be expired. */
1634 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1635 || IsProtected(channel
))
1638 /* Make sure there are no high-ranking users still in the channel. */
1639 for(user
=channel
->users
; user
; user
=user
->next
)
1640 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1645 /* Unregister the channel */
1646 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1647 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1648 unregister_channel(channel
, "registration expired.");
1651 if(chanserv_conf
.channel_expire_frequency
)
1652 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1656 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1658 char protect
= channel
->chOpts
[chProtect
];
1659 struct userData
*cs_victim
, *cs_aggressor
;
1661 /* Don't protect if no one is to be protected, someone is attacking
1662 himself, or if the aggressor is an IRC Operator. */
1663 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1666 /* Don't protect if the victim isn't authenticated (because they
1667 can't be a channel user), unless we are to protect non-users
1669 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1670 if(protect
!= 'a' && !cs_victim
)
1673 /* Protect if the aggressor isn't a user because at this point,
1674 the aggressor can only be less than or equal to the victim. */
1675 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1679 /* If the aggressor was a user, then the victim can't be helped. */
1686 if(cs_victim
->access
> cs_aggressor
->access
)
1691 if(cs_victim
->access
>= cs_aggressor
->access
)
1700 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1702 struct chanData
*cData
= channel
->channel_info
;
1703 struct userData
*cs_victim
;
1705 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1706 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1707 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1709 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1717 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1719 struct chanData
*cData
= channel
->channel_info
;
1720 struct userData
*cs_victim
;
1722 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1723 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1724 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1726 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1735 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1737 if(IsService(victim
))
1739 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1743 if(protect_user(victim
, user
, channel
->channel_info
))
1745 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1753 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1755 if(IsService(victim
))
1757 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1761 if(protect_user(victim
, user
, channel
->channel_info
))
1763 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1770 static struct do_not_register
*
1771 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1773 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1774 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1775 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1776 strcpy(dnr
->reason
, reason
);
1778 if(dnr
->chan_name
[0] == '*')
1779 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1780 else if(strpbrk(dnr
->chan_name
, "*?"))
1781 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1783 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1787 static struct dnrList
1788 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1790 struct dnrList list
;
1792 struct do_not_register
*dnr
;
1794 dnrList_init(&list
);
1795 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1796 dnrList_append(&list
, dnr
);
1797 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1798 dnrList_append(&list
, dnr
);
1800 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1801 if(match_ircglob(chan_name
, iter_key(it
)))
1802 dnrList_append(&list
, iter_data(it
));
1807 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1809 struct dnrList list
;
1810 struct do_not_register
*dnr
;
1812 char buf
[INTERVALLEN
];
1814 list
= chanserv_find_dnrs(chan_name
, handle
);
1815 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1817 dnr
= list
.list
[ii
];
1820 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1821 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1824 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1827 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1832 struct do_not_register
*
1833 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1835 struct do_not_register
*dnr
;
1838 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1842 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1844 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1845 if(match_ircglob(chan_name
, iter_key(it
)))
1846 return iter_data(it
);
1851 static CHANSERV_FUNC(cmd_noregister
)
1854 struct do_not_register
*dnr
;
1855 char buf
[INTERVALLEN
];
1856 unsigned int matches
;
1862 reply("CSMSG_DNR_SEARCH_RESULTS");
1863 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1866 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1868 dnr
= iter_data(it
);
1870 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1872 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1875 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1877 dnr
= iter_data(it
);
1879 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1881 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1884 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1886 dnr
= iter_data(it
);
1888 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1890 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1895 reply("MSG_MATCH_COUNT", matches
);
1897 reply("MSG_NO_MATCHES");
1903 if(!IsChannelName(target
) && (*target
!= '*'))
1905 reply("CSMSG_NOT_DNR", target
);
1911 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1912 if((*target
== '*') && !get_handle_info(target
+ 1))
1914 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1917 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1918 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1922 reply("CSMSG_DNR_SEARCH_RESULTS");
1923 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1926 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1928 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1930 reply("MSG_NO_MATCHES");
1934 static CHANSERV_FUNC(cmd_allowregister
)
1936 const char *chan_name
= argv
[1];
1938 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1940 dict_remove(handle_dnrs
, chan_name
+1);
1941 reply("CSMSG_DNR_REMOVED", chan_name
);
1943 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1945 dict_remove(plain_dnrs
, chan_name
);
1946 reply("CSMSG_DNR_REMOVED", chan_name
);
1948 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1950 dict_remove(mask_dnrs
, chan_name
);
1951 reply("CSMSG_DNR_REMOVED", chan_name
);
1955 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1962 chanserv_get_owned_count(struct handle_info
*hi
)
1964 struct userData
*cList
;
1967 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1968 if(cList
->access
== UL_OWNER
)
1973 static CHANSERV_FUNC(cmd_register
)
1975 struct handle_info
*handle
;
1976 struct chanData
*cData
;
1977 struct modeNode
*mn
;
1978 char reason
[MAXLEN
];
1980 unsigned int new_channel
, force
=0;
1981 struct do_not_register
*dnr
;
1987 if(channel
->channel_info
)
1989 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1993 if(channel
->bad_channel
)
1995 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1999 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2001 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2006 chan_name
= channel
->name
;
2012 reply("MSG_MISSING_PARAMS", cmd
->name
);
2013 svccmd_send_help_brief(user
, chanserv
, cmd
);
2016 if(!IsChannelName(argv
[1]))
2018 reply("MSG_NOT_CHANNEL_NAME");
2022 if(opserv_bad_channel(argv
[1]))
2024 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2029 chan_name
= argv
[1];
2032 if(argc
>= (new_channel
+2))
2034 if(!IsHelping(user
))
2036 reply("CSMSG_PROXY_FORBIDDEN");
2040 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2042 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2043 dnr
= chanserv_is_dnr(chan_name
, handle
);
2045 /* Check if they are over the limit.. */
2046 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2048 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2055 handle
= user
->handle_info
;
2056 dnr
= chanserv_is_dnr(chan_name
, handle
);
2057 /* Check if they are over the limit.. */
2058 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2060 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2063 /* Check if another service is in the channel */
2065 for(n
= 0; n
< channel
->members
.used
; n
++)
2067 mn
= channel
->members
.list
[n
];
2068 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2070 reply("CSMSG_ANOTHER_SERVICE");
2077 if(!IsHelping(user
))
2078 reply("CSMSG_DNR_CHANNEL", chan_name
);
2080 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2084 /* now handled above for message specilization *
2085 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2087 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2093 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2095 cData
= register_channel(channel
, user
->handle_info
->handle
);
2096 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2097 cData
->modes
= chanserv_conf
.default_modes
;
2099 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2100 if (IsOffChannel(cData
))
2102 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2106 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2107 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2108 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2110 mod_chanmode_announce(chanserv
, channel
, change
);
2111 mod_chanmode_free(change
);
2114 /* Initialize the channel's max user record. */
2115 cData
->max
= channel
->members
.used
;
2117 if(handle
!= user
->handle_info
)
2118 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2120 reply("CSMSG_REG_SUCCESS", channel
->name
);
2122 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2123 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2128 make_confirmation_string(struct userData
*uData
)
2130 static char strbuf
[16];
2135 for(src
= uData
->handle
->handle
; *src
; )
2136 accum
= accum
* 31 + toupper(*src
++);
2138 for(src
= uData
->channel
->channel
->name
; *src
; )
2139 accum
= accum
* 31 + toupper(*src
++);
2140 sprintf(strbuf
, "%08x", accum
);
2144 static CHANSERV_FUNC(cmd_unregister
)
2147 char reason
[MAXLEN
];
2148 struct chanData
*cData
;
2149 struct userData
*uData
;
2151 cData
= channel
->channel_info
;
2154 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2158 uData
= GetChannelUser(cData
, user
->handle_info
);
2159 if(!uData
|| (uData
->access
< UL_OWNER
))
2161 reply("CSMSG_NO_ACCESS");
2165 if(IsProtected(cData
))
2167 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2171 if(!IsHelping(user
))
2173 const char *confirm_string
;
2174 if(IsSuspended(cData
))
2176 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2179 confirm_string
= make_confirmation_string(uData
);
2180 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2182 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2187 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2188 name
= strdup(channel
->name
);
2189 unregister_channel(cData
, reason
);
2190 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2191 reply("CSMSG_UNREG_SUCCESS", name
);
2197 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2199 extern struct userNode
*spamserv
;
2200 struct mod_chanmode
*change
;
2202 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2204 change
= mod_chanmode_alloc(2);
2206 change
->args
[0].mode
= MODE_CHANOP
;
2207 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2208 change
->args
[1].mode
= MODE_CHANOP
;
2209 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2213 change
= mod_chanmode_alloc(1);
2215 change
->args
[0].mode
= MODE_CHANOP
;
2216 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2219 mod_chanmode_announce(chanserv
, channel
, change
);
2220 mod_chanmode_free(change
);
2223 static CHANSERV_FUNC(cmd_move
)
2225 struct mod_chanmode change
;
2226 struct chanNode
*target
;
2227 struct modeNode
*mn
;
2228 struct userData
*uData
;
2229 char reason
[MAXLEN
];
2230 struct do_not_register
*dnr
;
2231 int chanserv_join
= 0, spamserv_join
;
2235 if(IsProtected(channel
->channel_info
))
2237 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2241 if(!IsChannelName(argv
[1]))
2243 reply("MSG_NOT_CHANNEL_NAME");
2247 if(opserv_bad_channel(argv
[1]))
2249 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2253 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2255 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2257 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2259 if(!IsHelping(user
))
2260 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2262 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2268 mod_chanmode_init(&change
);
2269 if(!(target
= GetChannel(argv
[1])))
2271 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2272 if(!IsSuspended(channel
->channel_info
))
2275 else if(target
->channel_info
)
2277 reply("CSMSG_ALREADY_REGGED", target
->name
);
2280 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2281 && !IsHelping(user
))
2283 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2286 else if(!IsSuspended(channel
->channel_info
))
2291 /* Clear MODE_REGISTERED from old channel, add it to new. */
2293 change
.modes_clear
= MODE_REGISTERED
;
2294 mod_chanmode_announce(chanserv
, channel
, &change
);
2295 change
.modes_clear
= 0;
2296 change
.modes_set
= MODE_REGISTERED
;
2297 mod_chanmode_announce(chanserv
, target
, &change
);
2300 /* Move the channel_info to the target channel; it
2301 shouldn't be necessary to clear timeq callbacks
2302 for the old channel. */
2303 target
->channel_info
= channel
->channel_info
;
2304 target
->channel_info
->channel
= target
;
2305 channel
->channel_info
= NULL
;
2307 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2310 ss_cs_join_channel(target
, spamserv_join
);
2312 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2313 if(!IsSuspended(target
->channel_info
))
2315 char reason2
[MAXLEN
];
2316 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2317 DelChannelUser(chanserv
, channel
, reason2
, 0);
2319 UnlockChannel(channel
);
2320 LockChannel(target
);
2321 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2322 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2327 merge_users(struct chanData
*source
, struct chanData
*target
)
2329 struct userData
*suData
, *tuData
, *next
;
2335 /* Insert the source's users into the scratch area. */
2336 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2337 dict_insert(merge
, suData
->handle
->handle
, suData
);
2339 /* Iterate through the target's users, looking for
2340 users common to both channels. The lower access is
2341 removed from either the scratch area or target user
2343 for(tuData
= target
->users
; tuData
; tuData
= next
)
2345 struct userData
*choice
;
2347 next
= tuData
->next
;
2349 /* If a source user exists with the same handle as a target
2350 channel's user, resolve the conflict by removing one. */
2351 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2355 /* Pick the data we want to keep. */
2356 /* If the access is the same, use the later seen time. */
2357 if(suData
->access
== tuData
->access
)
2358 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2359 else /* Otherwise, keep the higher access level. */
2360 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2362 /* Remove the user that wasn't picked. */
2363 if(choice
== tuData
)
2365 dict_remove(merge
, suData
->handle
->handle
);
2366 del_channel_user(suData
, 0);
2369 del_channel_user(tuData
, 0);
2372 /* Move the remaining users to the target channel. */
2373 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2375 suData
= iter_data(it
);
2377 /* Insert the user into the target channel's linked list. */
2378 suData
->prev
= NULL
;
2379 suData
->next
= target
->users
;
2380 suData
->channel
= target
;
2383 target
->users
->prev
= suData
;
2384 target
->users
= suData
;
2386 /* Update the user counts for the target channel; the
2387 source counts are left alone. */
2388 target
->userCount
++;
2391 /* Possible to assert (source->users == NULL) here. */
2392 source
->users
= NULL
;
2397 merge_bans(struct chanData
*source
, struct chanData
*target
)
2399 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2401 /* Hold on to the original head of the target ban list
2402 to avoid comparing source bans with source bans. */
2403 tFront
= target
->bans
;
2405 /* Perform a totally expensive O(n*m) merge, ick. */
2406 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2408 /* Flag to track whether the ban's been moved
2409 to the destination yet. */
2412 /* Possible to assert (sbData->prev == NULL) here. */
2413 sNext
= sbData
->next
;
2415 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2417 tNext
= tbData
->next
;
2419 /* Perform two comparisons between each source
2420 and target ban, conflicts are resolved by
2421 keeping the broader ban and copying the later
2422 expiration and triggered time. */
2423 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2425 /* There is a broader ban in the target channel that
2426 overrides one in the source channel; remove the
2427 source ban and break. */
2428 if(sbData
->expires
> tbData
->expires
)
2429 tbData
->expires
= sbData
->expires
;
2430 if(sbData
->triggered
> tbData
->triggered
)
2431 tbData
->triggered
= sbData
->triggered
;
2432 del_channel_ban(sbData
);
2435 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2437 /* There is a broader ban in the source channel that
2438 overrides one in the target channel; remove the
2439 target ban, fall through and move the source over. */
2440 if(tbData
->expires
> sbData
->expires
)
2441 sbData
->expires
= tbData
->expires
;
2442 if(tbData
->triggered
> sbData
->triggered
)
2443 sbData
->triggered
= tbData
->triggered
;
2444 if(tbData
== tFront
)
2446 del_channel_ban(tbData
);
2449 /* Source bans can override multiple target bans, so
2450 we allow a source to run through this loop multiple
2451 times, but we can only move it once. */
2456 /* Remove the source ban from the source ban list. */
2458 sbData
->next
->prev
= sbData
->prev
;
2460 /* Modify the source ban's associated channel. */
2461 sbData
->channel
= target
;
2463 /* Insert the ban into the target channel's linked list. */
2464 sbData
->prev
= NULL
;
2465 sbData
->next
= target
->bans
;
2468 target
->bans
->prev
= sbData
;
2469 target
->bans
= sbData
;
2471 /* Update the user counts for the target channel. */
2476 /* Possible to assert (source->bans == NULL) here. */
2477 source
->bans
= NULL
;
2481 merge_data(struct chanData
*source
, struct chanData
*target
)
2483 /* Use more recent visited and owner-transfer time; use older
2484 * registered time. Bitwise or may_opchan. Use higher max.
2485 * Do not touch last_refresh, ban count or user counts.
2487 if(source
->visited
> target
->visited
)
2488 target
->visited
= source
->visited
;
2489 if(source
->registered
< target
->registered
)
2490 target
->registered
= source
->registered
;
2491 if(source
->ownerTransfer
> target
->ownerTransfer
)
2492 target
->ownerTransfer
= source
->ownerTransfer
;
2493 if(source
->may_opchan
)
2494 target
->may_opchan
= 1;
2495 if(source
->max
> target
->max
)
2496 target
->max
= source
->max
;
2500 merge_channel(struct chanData
*source
, struct chanData
*target
)
2502 merge_users(source
, target
);
2503 merge_bans(source
, target
);
2504 merge_data(source
, target
);
2507 static CHANSERV_FUNC(cmd_merge
)
2509 struct userData
*target_user
;
2510 struct chanNode
*target
;
2511 char reason
[MAXLEN
];
2515 /* Make sure the target channel exists and is registered to the user
2516 performing the command. */
2517 if(!(target
= GetChannel(argv
[1])))
2519 reply("MSG_INVALID_CHANNEL");
2523 if(!target
->channel_info
)
2525 reply("CSMSG_NOT_REGISTERED", target
->name
);
2529 if(IsProtected(channel
->channel_info
))
2531 reply("CSMSG_MERGE_NODELETE");
2535 if(IsSuspended(target
->channel_info
))
2537 reply("CSMSG_MERGE_SUSPENDED");
2541 if(channel
== target
)
2543 reply("CSMSG_MERGE_SELF");
2547 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2548 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2550 reply("CSMSG_MERGE_NOT_OWNER");
2554 /* Merge the channel structures and associated data. */
2555 merge_channel(channel
->channel_info
, target
->channel_info
);
2556 spamserv_cs_move_merge(user
, channel
, target
, 0);
2557 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2558 unregister_channel(channel
->channel_info
, reason
);
2559 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2563 static CHANSERV_FUNC(cmd_opchan
)
2565 struct mod_chanmode change
;
2566 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2568 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2571 channel
->channel_info
->may_opchan
= 0;
2572 mod_chanmode_init(&change
);
2574 change
.args
[0].mode
= MODE_CHANOP
;
2575 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2576 mod_chanmode_announce(chanserv
, channel
, &change
);
2577 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2581 static CHANSERV_FUNC(cmd_adduser
)
2583 struct userData
*actee
;
2584 struct userData
*actor
;
2585 struct handle_info
*handle
;
2586 unsigned short access
;
2590 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2592 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2596 access
= user_level_from_name(argv
[2], UL_OWNER
);
2599 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2603 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2604 if(actor
->access
<= access
)
2606 reply("CSMSG_NO_BUMP_ACCESS");
2610 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2612 // 'kevin must first authenticate with AuthServ.' is sent to user
2613 struct userNode
*unode
;
2614 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2617 if(find_adduser_pending(channel
, unode
)) {
2618 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2621 if(IsInChannel(channel
, unode
)) {
2622 reply("CSMSG_ADDUSER_PENDING");
2623 add_adduser_pending(channel
, unode
, access
);
2624 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2626 /* this results in user must auth AND not in chan errors. too confusing..
2628 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2636 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2638 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2642 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2643 scan_user_presence(actee
, NULL
);
2644 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2648 static CHANSERV_FUNC(cmd_clvl
)
2650 struct handle_info
*handle
;
2651 struct userData
*victim
;
2652 struct userData
*actor
;
2653 unsigned short new_access
;
2654 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2658 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2660 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2663 if(handle
== user
->handle_info
&& !privileged
)
2665 reply("CSMSG_NO_SELF_CLVL");
2669 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2671 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2675 if(actor
->access
<= victim
->access
&& !privileged
)
2677 reply("MSG_USER_OUTRANKED", handle
->handle
);
2681 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2685 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2689 if(new_access
>= actor
->access
&& !privileged
)
2691 reply("CSMSG_NO_BUMP_ACCESS");
2695 victim
->access
= new_access
;
2696 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2700 static CHANSERV_FUNC(cmd_deluser
)
2702 struct handle_info
*handle
;
2703 struct userData
*victim
;
2704 struct userData
*actor
;
2705 unsigned short access
;
2710 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2712 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2715 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2717 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2723 access
= user_level_from_name(argv
[1], UL_OWNER
);
2726 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2729 if(access
!= victim
->access
)
2731 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2737 access
= victim
->access
;
2740 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2742 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2746 chan_name
= strdup(channel
->name
);
2747 del_channel_user(victim
, 1);
2748 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2754 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2756 struct userData
*actor
, *uData
, *next
;
2758 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2760 if(min_access
> max_access
)
2762 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2766 if((actor
->access
<= max_access
) && !IsHelping(user
))
2768 reply("CSMSG_NO_ACCESS");
2772 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2776 if((uData
->access
>= min_access
)
2777 && (uData
->access
<= max_access
)
2778 && match_ircglob(uData
->handle
->handle
, mask
))
2779 del_channel_user(uData
, 1);
2782 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2786 static CHANSERV_FUNC(cmd_mdelowner
)
2788 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2791 static CHANSERV_FUNC(cmd_mdelcoowner
)
2793 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2796 static CHANSERV_FUNC(cmd_mdelmanager
)
2798 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2801 static CHANSERV_FUNC(cmd_mdelop
)
2803 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2806 static CHANSERV_FUNC(cmd_mdelpeon
)
2808 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2811 static CHANSERV_FUNC(cmd_mdelhalfop
)
2813 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2819 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2821 struct banData
*bData
, *next
;
2822 char interval
[INTERVALLEN
];
2827 limit
= now
- duration
;
2828 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2832 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2835 del_channel_ban(bData
);
2839 intervalString(interval
, duration
, user
->handle_info
);
2840 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2845 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2847 struct userData
*actor
, *uData
, *next
;
2848 char interval
[INTERVALLEN
];
2852 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2853 if(min_access
> max_access
)
2855 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2859 if((actor
->access
<= max_access
) && !IsHelping(user
))
2861 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2866 limit
= now
- duration
;
2867 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2871 if((uData
->seen
> limit
)
2873 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2876 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2877 || (!max_access
&& (uData
->access
< actor
->access
)))
2879 del_channel_user(uData
, 1);
2887 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2889 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2893 static CHANSERV_FUNC(cmd_trim
)
2895 unsigned long duration
;
2896 unsigned short min_level
, max_level
;
2901 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2902 duration
= ParseInterval(argv
[2]);
2905 reply("CSMSG_CANNOT_TRIM");
2909 if(!irccasecmp(argv
[1], "lamers"))
2911 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2914 else if(!irccasecmp(argv
[1], "users"))
2916 cmd_trim_users(user
, channel
, 0, 0, duration
, vacation
);
2919 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2921 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
, vacation
);
2924 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2926 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
, vacation
);
2931 reply("CSMSG_INVALID_TRIM", argv
[1]);
2936 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2937 to the user. cmd_all takes advantage of this. */
2938 static CHANSERV_FUNC(cmd_up
)
2940 struct mod_chanmode change
;
2941 struct userData
*uData
;
2944 mod_chanmode_init(&change
);
2946 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2947 if(!change
.args
[0].u
.member
)
2950 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2954 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2958 reply("CSMSG_GODMODE_UP", argv
[0]);
2961 else if(uData
->access
>= UL_OP
)
2963 change
.args
[0].mode
= MODE_CHANOP
;
2964 errmsg
= "CSMSG_ALREADY_OPPED";
2966 else if(uData
->access
>= UL_HALFOP
)
2968 change
.args
[0].mode
= MODE_HALFOP
;
2969 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2971 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2973 change
.args
[0].mode
= MODE_VOICE
;
2974 errmsg
= "CSMSG_ALREADY_VOICED";
2979 reply("CSMSG_NO_ACCESS");
2982 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2983 if(!change
.args
[0].mode
)
2986 reply(errmsg
, channel
->name
);
2989 modcmd_chanmode_announce(&change
);
2993 static CHANSERV_FUNC(cmd_down
)
2995 struct mod_chanmode change
;
2997 mod_chanmode_init(&change
);
2999 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3000 if(!change
.args
[0].u
.member
)
3003 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3007 if(!change
.args
[0].u
.member
->modes
)
3010 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3014 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3015 modcmd_chanmode_announce(&change
);
3019 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
)
3021 struct userData
*cList
;
3023 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3025 if(IsSuspended(cList
->channel
)
3026 || IsUserSuspended(cList
)
3027 || !GetUserMode(cList
->channel
->channel
, user
))
3030 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3036 static CHANSERV_FUNC(cmd_upall
)
3038 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3041 static CHANSERV_FUNC(cmd_downall
)
3043 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3046 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3047 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3050 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
)
3052 unsigned int ii
, valid
;
3053 struct userNode
*victim
;
3054 struct mod_chanmode
*change
;
3056 change
= mod_chanmode_alloc(argc
- 1);
3058 for(ii
=valid
=0; ++ii
< argc
; )
3060 if(!(victim
= GetUserH(argv
[ii
])))
3062 change
->args
[valid
].mode
= mode
;
3063 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3064 if(!change
->args
[valid
].u
.member
)
3066 if(validate
&& !validate(user
, channel
, victim
))
3071 change
->argc
= valid
;
3072 if(valid
< (argc
-1))
3073 reply("CSMSG_PROCESS_FAILED");
3076 modcmd_chanmode_announce(change
);
3077 reply(action
, channel
->name
);
3079 mod_chanmode_free(change
);
3083 static CHANSERV_FUNC(cmd_op
)
3085 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3088 static CHANSERV_FUNC(cmd_hop
)
3090 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3093 static CHANSERV_FUNC(cmd_deop
)
3095 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3098 static CHANSERV_FUNC(cmd_dehop
)
3100 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3103 static CHANSERV_FUNC(cmd_voice
)
3105 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3108 static CHANSERV_FUNC(cmd_devoice
)
3110 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3114 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3120 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3122 struct modeNode
*mn
= channel
->members
.list
[ii
];
3124 if(IsService(mn
->user
))
3127 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3130 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3134 victims
[(*victimCount
)++] = mn
;
3139 #define i_isdigit(x) isdigit((int) (unsigned char) (x))
3141 int is_ipv4_address(const char *host
)
3143 while (*host
!= '\0') {
3144 if (*host
!= '.' && !i_isdigit(*host
))
3151 static char *get_domain_mask(char *host
)
3155 if (strchr(host
, '.') == NULL
) {
3156 /* no dots - toplevel domain or IPv6 address */
3157 ptr
= strrchr(host
, ':');
3159 /* IPv6 address, ban the last 64k addresses */
3160 if (ptr
[1] != '\0') strcpy(ptr
+1, "*");
3165 if (is_ipv4_address(host
)) {
3166 /* it's an IP address, change last digit to * */
3167 ptr
= strrchr(host
, '.');
3168 if (ptr
!= NULL
&& i_isdigit(ptr
[1]))
3171 /* if more than one dot, skip the first
3172 (dyn123.blah.net -> *.blah.net) */
3173 ptr
= strchr(host
, '.');
3174 if (ptr
!= NULL
&& strchr(ptr
+1, '.') != NULL
) {
3182 char *generate_ban_hostmask(struct userNode
*user
, const char banopt
)
3184 char *nickname
= NULL
;
3186 char *hostname
= NULL
;
3188 char *usemask
= NULL
;
3191 usemask
= user
->hostname
;
3192 if (IsFakeHost(user
) && IsHiddenHost(user
))
3193 usemask
= user
->fakehost
;
3194 else if (IsSetHost(user
))
3195 usemask
= strchr(user
->sethost
, '@') + 1;
3196 else if (IsHiddenHost(user
) && user
->handle_info
&& hidden_host_suffix
) {
3197 usemask
= alloca(strlen(user
->handle_info
->handle
) + strlen(hidden_host_suffix
) + 2);
3198 sprintf(usemask
, "%s.%s", user
->handle_info
->handle
, hidden_host_suffix
);
3201 if((banopt
== '6') || (banopt
== '7') || (banopt
== '8') || (banopt
== '9'))
3202 nickname
= user
->nick
;
3206 if((banopt
== '4') || (banopt
== '5') || (banopt
== '8') || (banopt
== '9'))
3207 hostname
= get_domain_mask(usemask
);
3211 if((banopt
== '1') || (banopt
== '6')) {
3212 if (IsSetHost(user
)) {
3213 ident
= alloca(strcspn(user
->sethost
, "@")+2);
3214 safestrncpy(ident
, user
->sethost
, strcspn(user
->sethost
, "@")+1);
3216 ident
= user
->ident
;
3218 else if((banopt
== '2') || (banopt
== '4') || (banopt
== '8')) {
3219 if (IsSetHost(user
)) {
3220 ident
= alloca(strcspn(user
->sethost
, "@")+3);
3222 safestrncpy(ident
+1, user
->sethost
, strcspn(user
->sethost
, "@")+1);
3224 ident
= malloc(strlen(user
->ident
)+1);
3225 sprintf(ident
, "*%s", user
->ident
);
3230 /* Put it all together. */
3231 len
= strlen(ident
) + strlen(hostname
) + strlen(nickname
) + 3;
3233 sprintf(mask
, "%s!%s@%s", nickname
, ident
, hostname
);
3239 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3241 struct userNode
*victim
;
3242 struct modeNode
**victims
;
3243 struct chanData
*cData
;
3244 unsigned int offset
, n
, victimCount
, duration
= 0;
3245 char *reason
= "Bye.", *ban
, *name
, banopt
;
3246 char interval
[INTERVALLEN
];
3248 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3249 REQUIRE_PARAMS(offset
);
3252 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3253 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3255 /* Truncate the reason to a length of TOPICLEN, as
3256 the ircd does; however, leave room for an ellipsis
3257 and the kicker's nick. */
3258 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3262 if((victim
= GetUserH(argv
[1])))
3264 victims
= alloca(sizeof(victims
[0]));
3265 victims
[0] = GetUserMode(channel
, victim
);
3266 /* XXX: The comparison with ACTION_KICK is just because all
3267 * other actions can work on users outside the channel, and we
3268 * want to allow those (e.g. unbans) in that case. If we add
3269 * some other ejection action for in-channel users, change
3271 victimCount
= victims
[0] ? 1 : 0;
3273 if(IsService(victim
))
3276 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3280 if((action
== ACTION_KICK
) && !victimCount
)
3283 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3287 if(protect_user(victim
, user
, channel
->channel_info
))
3289 // This translates to send_message(user, cmd->parent->bot, ...)
3290 // if user is x3 (ctcp action) cmd is null and segfault.
3292 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3296 if(!(cData
= channel
->channel_info
)) {
3300 if (!(cData
->chOpts
[chBanType
])) {
3303 banopt
= cData
->chOpts
[chBanType
];
3306 ban
= generate_ban_hostmask(victim
, banopt
);
3307 name
= victim
->nick
;
3311 if(!is_ircmask(argv
[1]))
3314 reply("MSG_NICK_UNKNOWN", argv
[1]);
3318 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3320 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3323 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3326 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3327 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3329 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3330 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3331 some creativity, but its not x3's job to be the ban censor anyway. */
3332 if(is_overmask(argv
[1]))
3335 reply("CSMSG_LAME_MASK", argv
[1]);
3339 if((action
== ACTION_KICK
) && (victimCount
== 0))
3342 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3346 name
= ban
= strdup(argv
[1]);
3349 /* Truncate the ban in place if necessary; we must ensure
3350 that 'ban' is a valid ban mask before sanitizing it. */
3351 sanitize_ircmask(ban
);
3353 if(action
& ACTION_ADD_LAMER
)
3355 struct banData
*bData
, *next
;
3357 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3360 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3365 if(action
& ACTION_ADD_TIMED_LAMER
)
3367 duration
= ParseInterval(argv
[2]);
3372 reply("CSMSG_DURATION_TOO_LOW");
3376 else if(duration
> (86400 * 365 * 2))
3379 reply("CSMSG_DURATION_TOO_HIGH");
3386 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3388 if(match_ircglobs(bData
->mask
, ban
))
3390 int exact
= !irccasecmp(bData
->mask
, ban
);
3392 /* The ban is redundant; there is already a ban
3393 with the same effect in place. */
3397 free(bData
->reason
);
3398 bData
->reason
= strdup(reason
);
3399 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3401 reply("CSMSG_REASON_CHANGE", ban
);
3405 if(exact
&& bData
->expires
)
3409 /* If the ban matches an existing one exactly,
3410 extend the expiration time if the provided
3411 duration is longer. */
3412 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3414 bData
->expires
= now
+ duration
;
3425 /* Delete the expiration timeq entry and
3426 requeue if necessary. */
3427 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3430 timeq_add(bData
->expires
, expire_ban
, bData
);
3434 /* automated kickban, dont reply */
3437 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3439 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3445 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3452 if(match_ircglobs(ban
, bData
->mask
))
3454 /* The ban we are adding makes previously existing
3455 bans redundant; silently remove them. */
3456 del_channel_ban(bData
);
3460 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
);
3462 name
= ban
= strdup(bData
->mask
);
3466 /* WHAT DOES THIS DO?? -Rubin */
3467 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3469 extern const char *hidden_host_suffix
;
3470 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3472 unsigned int l1
, l2
;
3475 l2
= strlen(old_name
);
3478 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3480 new_mask
= malloc(MAXLEN
);
3481 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3483 name
= ban
= new_mask
;
3488 if(action
& ACTION_BAN
)
3490 unsigned int exists
;
3491 struct mod_chanmode
*change
;
3493 if(channel
->banlist
.used
>= MAXBANS
)
3496 reply("CSMSG_BANLIST_FULL", channel
->name
);
3501 exists
= ChannelBanExists(channel
, ban
);
3502 change
= mod_chanmode_alloc(victimCount
+ 1);
3503 for(n
= 0; n
< victimCount
; ++n
)
3505 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3506 change
->args
[n
].u
.member
= victims
[n
];
3510 change
->args
[n
].mode
= MODE_BAN
;
3511 change
->args
[n
++].u
.hostmask
= ban
;
3515 modcmd_chanmode_announce(change
);
3517 mod_chanmode_announce(chanserv
, channel
, change
);
3518 mod_chanmode_free(change
);
3520 if(exists
&& (action
== ACTION_BAN
))
3523 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3529 if(action
& ACTION_KICK
)
3531 char kick_reason
[MAXLEN
];
3532 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3534 for(n
= 0; n
< victimCount
; n
++)
3535 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3540 /* No response, since it was automated. */
3542 else if(action
& ACTION_ADD_LAMER
)
3545 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3547 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3549 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3550 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3551 else if(action
& ACTION_BAN
)
3552 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3553 else if(action
& ACTION_KICK
&& victimCount
)
3554 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3560 static CHANSERV_FUNC(cmd_kickban
)
3562 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3565 static CHANSERV_FUNC(cmd_kick
)
3567 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3570 static CHANSERV_FUNC(cmd_ban
)
3572 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3575 static CHANSERV_FUNC(cmd_addlamer
)
3577 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3580 static CHANSERV_FUNC(cmd_addtimedlamer
)
3582 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3585 static struct mod_chanmode
*
3586 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3588 struct mod_chanmode
*change
;
3589 unsigned char *match
;
3590 unsigned int ii
, count
;
3592 match
= alloca(bans
->used
);
3595 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3597 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3598 MATCH_USENICK
| MATCH_VISIBLE
);
3605 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3607 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3614 change
= mod_chanmode_alloc(count
);
3615 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3619 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3620 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3622 assert(count
== change
->argc
);
3626 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3628 unsigned int jj
, ii
, count
;
3630 struct chanData
*channel
;
3632 struct mod_chanmode
*change
;
3634 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3635 /* Walk through every channel */
3636 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3637 switch(channel
->chOpts
[chBanTimeout
])
3639 default: case '0': continue; /* Dont remove bans in this chan */
3640 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3641 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3642 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3643 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3644 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3647 /* First find out how many bans were going to unset */
3648 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3649 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3653 /* At least one ban, so setup a removal */
3654 change
= mod_chanmode_alloc(count
);
3656 /* Walk over every ban in this channel.. */
3657 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3658 bn
= channel
->channel
->banlist
.list
[jj
];
3659 if (bn
->set
< bantimeout
) {
3660 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3662 /* Add this ban to the mode change */
3663 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3664 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3666 /* Pull this ban out of the list */
3667 banList_remove(&(channel
->channel
->banlist
), bn
);
3672 /* Send the modes to IRC */
3673 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3675 /* free memory from strdup above */
3676 for(ii
= 0; ii
< count
; ++ii
)
3677 free((char*)change
->args
[ii
].u
.hostmask
);
3679 mod_chanmode_free(change
);
3682 /* Set this function to run again */
3683 if(chanserv_conf
.ban_timeout_frequency
)
3684 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3689 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3691 struct userNode
*actee
;
3697 /* may want to allow a comma delimited list of users... */
3698 if(!(actee
= GetUserH(argv
[1])))
3700 if(!is_ircmask(argv
[1]))
3702 reply("MSG_NICK_UNKNOWN", argv
[1]);
3706 mask
= strdup(argv
[1]);
3709 /* We don't sanitize the mask here because ircu
3711 if(action
& ACTION_UNBAN
)
3713 struct mod_chanmode
*change
;
3714 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3719 modcmd_chanmode_announce(change
);
3720 for(ii
= 0; ii
< change
->argc
; ++ii
)
3721 free((char*)change
->args
[ii
].u
.hostmask
);
3722 mod_chanmode_free(change
);
3727 if(action
& ACTION_DEL_LAMER
)
3729 struct banData
*ban
, *next
;
3731 ban
= channel
->channel_info
->bans
; /* lamers */
3735 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3738 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3743 del_channel_ban(ban
);
3750 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3752 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3758 static CHANSERV_FUNC(cmd_unban
)
3760 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3763 static CHANSERV_FUNC(cmd_dellamer
)
3765 /* it doesn't necessarily have to remove the channel ban - may want
3766 to make that an option. */
3767 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3770 static CHANSERV_FUNC(cmd_unbanme
)
3772 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3773 long flags
= ACTION_UNBAN
;
3775 /* remove permanent bans if the user has the proper access. */
3776 if(uData
->access
>= UL_MANAGER
)
3777 flags
|= ACTION_DEL_LAMER
;
3779 argv
[1] = user
->nick
;
3780 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3783 static CHANSERV_FUNC(cmd_unbanall
)
3785 struct mod_chanmode
*change
;
3788 if(!channel
->banlist
.used
)
3790 reply("CSMSG_NO_BANS", channel
->name
);
3794 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3795 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3797 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3798 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3800 modcmd_chanmode_announce(change
);
3801 for(ii
= 0; ii
< change
->argc
; ++ii
)
3802 free((char*)change
->args
[ii
].u
.hostmask
);
3803 mod_chanmode_free(change
);
3804 reply("CSMSG_BANS_REMOVED", channel
->name
);
3808 static CHANSERV_FUNC(cmd_open
)
3810 struct mod_chanmode
*change
;
3813 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3815 change
= mod_chanmode_alloc(0);
3816 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3817 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3818 && channel
->channel_info
->modes
.modes_set
)
3819 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3820 modcmd_chanmode_announce(change
);
3821 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3822 for(ii
= 0; ii
< change
->argc
; ++ii
)
3823 free((char*)change
->args
[ii
].u
.hostmask
);
3824 mod_chanmode_free(change
);
3828 static CHANSERV_FUNC(cmd_myaccess
)
3830 static struct string_buffer sbuf
;
3831 struct handle_info
*target_handle
;
3832 struct userData
*uData
;
3835 target_handle
= user
->handle_info
;
3836 else if(!IsHelping(user
))
3838 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3841 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3844 if(!target_handle
->channels
)
3846 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3850 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3851 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3853 struct chanData
*cData
= uData
->channel
;
3855 if(uData
->access
> UL_OWNER
)
3857 if(IsProtected(cData
)
3858 && (target_handle
!= user
->handle_info
)
3859 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3862 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3863 if(uData
->flags
== USER_AUTO_OP
)
3864 string_buffer_append(&sbuf
, ',');
3865 if(IsUserSuspended(uData
))
3866 string_buffer_append(&sbuf
, 's');
3867 if(IsUserAutoOp(uData
))
3869 if(uData
->access
>= UL_OP
)
3870 string_buffer_append(&sbuf
, 'o');
3871 else if(uData
->access
>= UL_HALFOP
)
3872 string_buffer_append(&sbuf
, 'h');
3873 else if(uData
->access
>= UL_PEON
)
3874 string_buffer_append(&sbuf
, 'v');
3876 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3877 string_buffer_append(&sbuf
, 'i');
3879 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3881 string_buffer_append_string(&sbuf
, ")]");
3882 string_buffer_append(&sbuf
, '\0');
3883 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3889 static CHANSERV_FUNC(cmd_access
)
3891 struct userNode
*target
;
3892 struct handle_info
*target_handle
;
3893 struct userData
*uData
;
3895 char prefix
[MAXLEN
];
3900 target_handle
= target
->handle_info
;
3902 else if((target
= GetUserH(argv
[1])))
3904 target_handle
= target
->handle_info
;
3906 else if(argv
[1][0] == '*')
3908 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3910 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3916 reply("MSG_NICK_UNKNOWN", argv
[1]);
3920 assert(target
|| target_handle
);
3922 if(target
== chanserv
)
3924 reply("CSMSG_IS_CHANSERV");
3932 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3937 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3940 reply("MSG_AUTHENTICATE");
3946 const char *epithet
= NULL
, *type
= NULL
;
3949 epithet
= chanserv_conf
.irc_operator_epithet
;
3952 else if(IsNetworkHelper(target
))
3954 epithet
= chanserv_conf
.network_helper_epithet
;
3955 type
= "network helper";
3957 else if(IsSupportHelper(target
))
3959 epithet
= chanserv_conf
.support_helper_epithet
;
3960 type
= "support helper";
3964 if(target_handle
->epithet
)
3965 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3967 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3969 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3973 sprintf(prefix
, "%s", target_handle
->handle
);
3976 if(!channel
->channel_info
)
3978 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3982 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3983 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3984 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3986 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3987 /* To prevent possible information leaks, only show infolines
3988 * if the requestor is in the channel or it's their own
3990 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3992 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3994 /* Likewise, only say it's suspended if the user has active
3995 * access in that channel or it's their own entry. */
3996 if(IsUserSuspended(uData
)
3997 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3998 || (user
->handle_info
== uData
->handle
)))
4000 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
4005 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
4011 /* This is never used...
4013 zoot_list(struct listData *list)
4015 struct userData *uData;
4016 unsigned int start, curr, highest, lowest;
4017 struct helpfile_table tmp_table;
4018 const char **temp, *msg;
4020 if(list->table.length == 1)
4023 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);
4025 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));
4026 msg = user_find_message(list->user, "MSG_NONE");
4027 send_message_type(4, list->user, list->bot, " %s", msg);
4029 tmp_table.width = list->table.width;
4030 tmp_table.flags = list->table.flags;
4031 list->table.contents[0][0] = " ";
4032 highest = list->highest;
4033 if(list->lowest != 0)
4034 lowest = list->lowest;
4035 else if(highest < 100)
4038 lowest = highest - 100;
4039 for(start = curr = 1; curr < list->table.length; )
4041 uData = list->users[curr-1];
4042 list->table.contents[curr++][0] = " ";
4043 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4046 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);
4048 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));
4049 temp = list->table.contents[--start];
4050 list->table.contents[start] = list->table.contents[0];
4051 tmp_table.contents = list->table.contents + start;
4052 tmp_table.length = curr - start;
4053 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4054 list->table.contents[start] = temp;
4056 highest = lowest - 1;
4057 lowest = (highest < 100) ? 0 : (highest - 99);
4064 normal_list(struct listData
*list
)
4068 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
);
4070 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
));
4071 if(list
->table
.length
== 1)
4073 msg
= user_find_message(list
->user
, "MSG_NONE");
4074 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4077 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4080 /* if these need changed, uncomment and customize
4082 clean_list(struct listData *list)
4086 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);
4088 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));
4089 if(list->table.length == 1)
4091 msg = user_find_message(list->user, "MSG_NONE");
4092 send_message_type(4, list->user, list->bot, " %s", msg);
4095 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4099 advanced_list(struct listData *list)
4103 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);
4105 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));
4106 if(list->table.length == 1)
4108 msg = user_find_message(list->user, "MSG_NONE");
4109 send_message_type(4, list->user, list->bot, " %s", msg);
4112 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4116 classic_list(struct listData *list)
4120 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4122 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4123 if(list->table.length == 1)
4125 msg = user_find_message(list->user, "MSG_NONE");
4126 send_message_type(4, list->user, list->bot, " %s", msg);
4129 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4134 userData_access_comp(const void *arg_a
, const void *arg_b
)
4136 const struct userData
*a
= *(struct userData
**)arg_a
;
4137 const struct userData
*b
= *(struct userData
**)arg_b
;
4139 if(a
->access
!= b
->access
)
4140 res
= b
->access
- a
->access
;
4142 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4147 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4149 void (*send_list
)(struct listData
*);
4150 struct userData
*uData
;
4151 struct listData lData
;
4152 unsigned int matches
;
4158 lData
.bot
= cmd
->parent
->bot
;
4159 lData
.channel
= channel
;
4160 lData
.lowest
= lowest
;
4161 lData
.highest
= highest
;
4162 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4163 send_list
= normal_list
;
4164 /* What does the following line do exactly?? */
4165 /*(void)zoot_list; ** since it doesn't show user levels */
4168 if(user->handle_info)
4170 switch(user->handle_info->userlist_style)
4172 case HI_STYLE_CLEAN:
4173 send_list = clean_list;
4175 case HI_STYLE_ADVANCED:
4176 send_list = advanced_list;
4178 case HI_STYLE_CLASSIC:
4179 send_list = classic_list;
4181 case HI_STYLE_NORMAL:
4183 send_list = normal_list;
4188 send_list
= normal_list
;
4190 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4192 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4194 if((uData
->access
< lowest
)
4195 || (uData
->access
> highest
)
4196 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4198 lData
.users
[matches
++] = uData
;
4200 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4202 lData
.table
.length
= matches
+1;
4203 lData
.table
.flags
= TABLE_NO_FREE
;
4204 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4206 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4207 lData
.table
.width
= 5; /* with level = 5 */
4209 lData
.table
.width
= 4; /* without = 4 */
4210 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4211 lData
.table
.contents
[0] = ary
;
4212 if(user
->handle_info
) {
4213 switch(user
->handle_info
->userlist_style
) {
4214 case HI_STYLE_CLASSIC
:
4217 case HI_STYLE_ADVANCED
:
4218 ary
[i
++] = "Access";
4221 case HI_STYLE_CLEAN
:
4222 ary
[i
++] = "Access";
4224 case HI_STYLE_NORMAL
:
4226 ary
[i
++] = "Access";
4231 ary
[i
++] = "Access";
4233 ary
[i
++] = "Account";
4234 ary
[i
] = "Last Seen";
4236 ary
[i
++] = "Status";
4237 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4239 struct userData
*uData
= lData
.users
[matches
-1];
4240 char seen
[INTERVALLEN
];
4243 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4244 lData
.table
.contents
[matches
] = ary
;
4245 if(user
->handle_info
) {
4246 switch(user
->handle_info
->userlist_style
) {
4247 case HI_STYLE_CLASSIC
:
4248 ary
[i
++] = strtab(uData
->access
);
4250 case HI_STYLE_ADVANCED
:
4251 ary
[i
++] = user_level_name_from_level(uData
->access
);
4252 ary
[i
++] = strtab(uData
->access
);
4254 case HI_STYLE_CLEAN
:
4255 ary
[i
++] = user_level_name_from_level(uData
->access
);
4257 case HI_STYLE_NORMAL
:
4259 ary
[i
++] = user_level_name_from_level(uData
->access
);
4264 ary
[i
++] = user_level_name_from_level(uData
->access
);
4266 ary
[i
++] = uData
->handle
->handle
;
4269 else if(!uData
->seen
)
4272 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4273 ary
[i
] = strdup(ary
[i
]);
4275 if(IsUserSuspended(uData
))
4276 ary
[i
++] = "Suspended";
4277 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4278 ary
[i
++] = "Vacation";
4280 ary
[i
++] = "Normal";
4283 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4285 /* Free strdup above */
4286 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4287 free(lData
.table
.contents
[matches
]);
4289 free(lData
.table
.contents
[0]);
4290 free(lData
.table
.contents
);
4294 /* Remove this now that debugging is over? or improve it for
4295 * users? Would it be better tied into USERS somehow? -Rubin */
4296 static CHANSERV_FUNC(cmd_pending
)
4298 struct adduserPending
*ap
;
4299 reply("CSMSG_ADDUSER_PENDING_HEADER");
4300 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4302 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4303 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4304 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4308 static CHANSERV_FUNC(cmd_users
)
4310 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4313 static CHANSERV_FUNC(cmd_wlist
)
4315 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4318 static CHANSERV_FUNC(cmd_clist
)
4320 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4323 static CHANSERV_FUNC(cmd_mlist
)
4325 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4328 static CHANSERV_FUNC(cmd_olist
)
4330 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4333 static CHANSERV_FUNC(cmd_hlist
)
4335 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4338 static CHANSERV_FUNC(cmd_plist
)
4340 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4343 static CHANSERV_FUNC(cmd_lamers
)
4345 struct helpfile_table tbl
;
4346 unsigned int matches
= 0, timed
= 0, ii
;
4347 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4348 const char *msg_never
, *triggered
, *expires
;
4349 struct banData
*ban
, **bans
; /* lamers */
4356 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4357 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4360 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4362 if(search
&& !match_ircglobs(search
, ban
->mask
))
4364 bans
[matches
++] = ban
;
4369 tbl
.length
= matches
+ 1;
4370 tbl
.width
= 4 + timed
;
4372 tbl
.flags
= TABLE_NO_FREE
;
4373 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4374 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4375 tbl
.contents
[0][0] = "Mask";
4376 tbl
.contents
[0][1] = "Set By";
4377 tbl
.contents
[0][2] = "Triggered";
4380 tbl
.contents
[0][3] = "Expires";
4381 tbl
.contents
[0][4] = "Reason";
4384 tbl
.contents
[0][3] = "Reason";
4387 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4389 free(tbl
.contents
[0]);
4394 msg_never
= user_find_message(user
, "MSG_NEVER");
4395 for(ii
= 0; ii
< matches
; )
4401 else if(ban
->expires
)
4402 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4404 expires
= msg_never
;
4407 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4409 triggered
= msg_never
;
4411 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4412 tbl
.contents
[ii
][0] = ban
->mask
;
4413 tbl
.contents
[ii
][1] = ban
->owner
;
4414 tbl
.contents
[ii
][2] = strdup(triggered
);
4417 tbl
.contents
[ii
][3] = strdup(expires
);
4418 tbl
.contents
[ii
][4] = ban
->reason
;
4421 tbl
.contents
[ii
][3] = ban
->reason
;
4423 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4424 /* reply("MSG_MATCH_COUNT", matches); */
4425 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4427 free((char*)tbl
.contents
[ii
][2]);
4429 free((char*)tbl
.contents
[ii
][3]);
4430 free(tbl
.contents
[ii
]);
4432 free(tbl
.contents
[0]);
4439 * return + if the user does NOT have the right to set the topic, and
4440 * the topic is changed.
4443 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4445 struct chanData
*cData
= channel
->channel_info
;
4446 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4448 else if(cData
->topic
)
4449 return irccasecmp(new_topic
, cData
->topic
);
4456 * Makes a givin topic fit into a givin topic mask and returns
4459 * topic_mask - the mask to conform to
4460 * topic - the topic to make conform
4461 * new_topic - the pre-allocated char* to put the new topic into
4463 * modifies: new_topic
4466 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4468 //char *topic_mask = cData->topic_mask;
4470 int pos
=0, starpos
=-1, dpos
=0, len
;
4472 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4479 strcpy(new_topic
, "");
4482 len
= strlen(topic
);
4483 if((dpos
+ len
) > TOPICLEN
)
4484 len
= TOPICLEN
+ 1 - dpos
;
4485 memcpy(new_topic
+dpos
, topic
, len
);
4489 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4490 default: new_topic
[dpos
++] = tchar
; break;
4493 if((dpos
> TOPICLEN
) || tchar
)
4495 strcpy(new_topic
, "");
4498 new_topic
[dpos
] = 0;
4502 static CHANSERV_FUNC(cmd_topic
)
4504 struct chanData
*cData
;
4508 #ifdef WITH_PROTOCOL_P10
4512 cData
= channel
->channel_info
;
4517 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4518 reply("CSMSG_TOPIC_SET", cData
->topic
);
4522 reply("CSMSG_NO_TOPIC", channel
->name
);
4526 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4527 /* If they say "!topic *", use an empty topic. */
4528 if((topic
[0] == '*') && (topic
[1] == 0))
4531 if(bad_topic(channel
, user
, topic
))
4533 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4538 /* If there is a topicmask set, and the new topic doesnt match, make it */
4539 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4541 char *topic_mask
= cData
->topic_mask
;
4542 char new_topic
[TOPICLEN
+1];
4544 /* make a new topic fitting mask */
4545 conform_topic(topic_mask
, topic
, new_topic
);
4548 /* Topic couldnt fit into mask, was too long */
4549 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4550 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4553 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4555 else /* No mask set, just set the topic */
4556 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4559 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4561 /* Grab the topic and save it as the default topic. */
4563 cData
->topic
= strdup(channel
->topic
);
4569 static CHANSERV_FUNC(cmd_mode
)
4571 struct userData
*uData
;
4572 struct mod_chanmode
*change
;
4577 change
= &channel
->channel_info
->modes
;
4578 if(change
->modes_set
|| change
->modes_clear
) {
4579 modcmd_chanmode_announce(change
);
4580 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4582 reply("CSMSG_NO_MODES", channel
->name
);
4586 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4588 base_oplevel
= MAXOPLEVEL
;
4589 else if (uData
->access
>= UL_OWNER
)
4592 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4593 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4597 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4601 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4602 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4605 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4606 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4610 modcmd_chanmode_announce(change
);
4611 mod_chanmode_free(change
);
4612 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4616 static CHANSERV_FUNC(cmd_invite
)
4618 struct userData
*uData
;
4619 struct userNode
*invite
;
4621 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4625 if(!(invite
= GetUserH(argv
[1])))
4627 reply("MSG_NICK_UNKNOWN", argv
[1]);
4634 if(GetUserMode(channel
, invite
))
4636 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4644 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4645 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4648 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4650 irc_invite(chanserv
, invite
, channel
);
4652 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4657 static CHANSERV_FUNC(cmd_inviteme
)
4659 if(GetUserMode(channel
, user
))
4661 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4664 if(channel
->channel_info
4665 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4667 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4670 irc_invite(cmd
->parent
->bot
, user
, channel
);
4675 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4678 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4680 /* We display things based on two dimensions:
4681 * - Issue time: present or absent
4682 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4683 * (in order of precedence, so something both expired and revoked
4684 * only counts as revoked)
4686 combo
= (suspended
->issued
? 4 : 0)
4687 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4689 case 0: /* no issue time, indefinite expiration */
4690 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4692 case 1: /* no issue time, expires in future */
4693 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4694 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4696 case 2: /* no issue time, expired */
4697 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4698 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4700 case 3: /* no issue time, revoked */
4701 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4702 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4704 case 4: /* issue time set, indefinite expiration */
4705 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4706 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4708 case 5: /* issue time set, expires in future */
4709 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4710 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4711 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4713 case 6: /* issue time set, expired */
4714 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4715 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4716 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4718 case 7: /* issue time set, revoked */
4719 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4720 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4721 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4724 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4730 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4733 const char *fmt
= "%a %b %d %H:%M %Y";
4734 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4736 if(giveownership
->staff_issuer
)
4738 if(giveownership
->reason
)
4739 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4740 giveownership
->target
, giveownership
->target_access
,
4741 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4743 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4744 giveownership
->target
, giveownership
->target_access
,
4745 giveownership
->staff_issuer
, buf
);
4749 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4754 static CHANSERV_FUNC(cmd_info
)
4756 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4757 struct userData
*uData
, *owner
;
4758 struct chanData
*cData
;
4759 struct do_not_register
*dnr
;
4764 cData
= channel
->channel_info
;
4765 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4766 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4769 uData
= GetChannelUser(cData
, user
->handle_info
);
4770 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4772 mod_chanmode_format(&cData
->modes
, modes
);
4773 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4774 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4777 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4781 note
= iter_data(it
);
4782 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4785 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4786 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4789 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4790 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4791 if(owner
->access
== UL_OWNER
)
4792 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4793 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4794 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4795 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4797 privileged
= IsStaff(user
);
4799 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4800 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4801 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4803 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4804 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4806 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4808 struct suspended
*suspended
;
4809 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4810 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4811 show_suspension_info(cmd
, user
, suspended
);
4813 else if(IsSuspended(cData
))
4815 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4816 show_suspension_info(cmd
, user
, cData
->suspended
);
4818 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4820 struct giveownership
*giveownership
;
4821 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4822 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4823 show_giveownership_info(cmd
, user
, giveownership
);
4825 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4826 reply("CSMSG_CHANNEL_END");
4828 reply("CSMSG_CHANNEL_END_CLEAN");
4832 static CHANSERV_FUNC(cmd_netinfo
)
4834 extern time_t boot_time
;
4835 extern unsigned long burst_length
;
4836 char interval
[INTERVALLEN
];
4838 reply("CSMSG_NETWORK_INFO");
4839 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4840 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4841 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4842 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4843 reply("CSMSG_NETWORK_LAMERS", banCount
);
4844 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4845 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4846 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4851 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4853 struct helpfile_table table
;
4855 struct userNode
*user
;
4860 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4861 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4862 for(nn
=0; nn
<list
->used
; nn
++)
4864 user
= list
->list
[nn
];
4865 if(user
->modes
& skip_flags
)
4869 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4872 nick
= alloca(strlen(user
->nick
)+3);
4873 sprintf(nick
, "(%s)", user
->nick
);
4877 table
.contents
[table
.length
][0] = nick
;
4880 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4883 static CHANSERV_FUNC(cmd_ircops
)
4885 reply("CSMSG_STAFF_OPERS");
4886 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4890 static CHANSERV_FUNC(cmd_helpers
)
4892 reply("CSMSG_STAFF_HELPERS");
4893 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4897 static CHANSERV_FUNC(cmd_staff
)
4899 reply("CSMSG_NETWORK_STAFF");
4900 cmd_ircops(CSFUNC_ARGS
);
4901 cmd_helpers(CSFUNC_ARGS
);
4905 static CHANSERV_FUNC(cmd_peek
)
4907 struct modeNode
*mn
;
4908 char modes
[MODELEN
];
4910 struct helpfile_table table
;
4912 irc_make_chanmode(channel
, modes
);
4914 reply("CSMSG_PEEK_INFO", channel
->name
);
4915 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4917 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4918 reply("CSMSG_PEEK_MODES", modes
);
4919 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4923 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4924 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4925 for(n
= 0; n
< channel
->members
.used
; n
++)
4927 mn
= channel
->members
.list
[n
];
4928 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4930 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4931 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4936 reply("CSMSG_PEEK_OPS");
4937 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4940 reply("CSMSG_PEEK_NO_OPS");
4941 reply("CSMSG_PEEK_END");
4945 static MODCMD_FUNC(cmd_wipeinfo
)
4947 struct handle_info
*victim
;
4948 struct userData
*ud
, *actor
;
4951 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4952 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4954 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4956 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4959 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4961 reply("MSG_USER_OUTRANKED", victim
->handle
);
4967 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4972 resync_channel(struct chanNode
*channel
)
4974 struct mod_chanmode
*changes
;
4975 struct chanData
*cData
= channel
->channel_info
;
4976 unsigned int ii
, used
;
4978 /* 6 = worst case -ovh+ovh on everyone */
4979 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4980 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4982 struct modeNode
*mn
= channel
->members
.list
[ii
];
4983 struct userData
*uData
;
4985 if(IsService(mn
->user
))
4989 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4991 /* If the channel is in no-mode mode, de-mode EVERYONE */
4992 if(cData
->chOpts
[chAutomode
] == 'n')
4996 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4997 changes
->args
[used
++].u
.member
= mn
;
5000 else /* Give various userlevels their modes.. */
5002 if(uData
&& uData
->access
>= UL_OP
)
5004 if(!(mn
->modes
& MODE_CHANOP
))
5006 changes
->args
[used
].mode
= MODE_CHANOP
;
5007 changes
->args
[used
++].u
.member
= mn
;
5010 else if(uData
&& uData
->access
>= UL_HALFOP
)
5012 if(mn
->modes
& MODE_CHANOP
)
5014 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5015 changes
->args
[used
++].u
.member
= mn
;
5017 if(!(mn
->modes
& MODE_HALFOP
))
5019 changes
->args
[used
].mode
= MODE_HALFOP
;
5020 changes
->args
[used
++].u
.member
= mn
;
5023 else if(uData
&& uData
->access
>= UL_PEON
)
5025 if(mn
->modes
& MODE_CHANOP
)
5027 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5028 changes
->args
[used
++].u
.member
= mn
;
5030 if(mn
->modes
& MODE_HALFOP
)
5032 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5033 changes
->args
[used
++].u
.member
= mn
;
5035 /* Don't voice peons if were in mode m */
5036 if( cData
->chOpts
[chAutomode
] == 'm')
5038 if(mn
->modes
& MODE_VOICE
)
5040 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5041 changes
->args
[used
++].u
.member
= mn
;
5044 /* otherwise, make user they do have voice */
5045 else if(!(mn
->modes
& MODE_VOICE
))
5047 changes
->args
[used
].mode
= MODE_VOICE
;
5048 changes
->args
[used
++].u
.member
= mn
;
5051 else /* They arnt on the userlist.. */
5053 /* If we voice everyone, but they dont.. */
5054 if(cData
->chOpts
[chAutomode
] == 'v')
5056 /* Remove anything except v */
5057 if(mn
->modes
& ~MODE_VOICE
)
5059 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5060 changes
->args
[used
++].u
.member
= mn
;
5063 if(!(mn
->modes
& MODE_VOICE
))
5065 changes
->args
[used
].mode
= MODE_VOICE
;
5066 changes
->args
[used
++].u
.member
= mn
;
5069 /* If we hop everyone, but they dont.. */
5070 else if(cData
->chOpts
[chAutomode
] == 'h')
5072 /* Remove anything except h */
5073 if(mn
->modes
& ~MODE_HALFOP
)
5075 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5076 changes
->args
[used
++].u
.member
= mn
;
5079 if(!(mn
->modes
& MODE_HALFOP
))
5081 changes
->args
[used
].mode
= MODE_HALFOP
;
5082 changes
->args
[used
++].u
.member
= mn
;
5085 /* If we op everyone, but they dont.. */
5086 else if(cData
->chOpts
[chAutomode
] == 'o')
5088 /* Remove anything except h */
5089 if(mn
->modes
& ~MODE_CHANOP
)
5091 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5092 changes
->args
[used
++].u
.member
= mn
;
5095 if(!(mn
->modes
& MODE_CHANOP
))
5097 changes
->args
[used
].mode
= MODE_CHANOP
;
5098 changes
->args
[used
++].u
.member
= mn
;
5101 /* they have no excuse for having modes, de-everything them */
5106 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5107 changes
->args
[used
++].u
.member
= mn
;
5113 changes
->argc
= used
;
5114 mod_chanmode_announce(chanserv
, channel
, changes
);
5115 mod_chanmode_free(changes
);
5118 static CHANSERV_FUNC(cmd_resync
)
5120 resync_channel(channel
);
5121 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5125 static CHANSERV_FUNC(cmd_seen
)
5127 struct userData
*uData
;
5128 struct handle_info
*handle
;
5129 char seen
[INTERVALLEN
];
5133 if(!irccasecmp(argv
[1], chanserv
->nick
))
5135 reply("CSMSG_IS_CHANSERV");
5139 if(!(handle
= get_handle_info(argv
[1])))
5141 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5145 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5147 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5152 reply("CSMSG_USER_PRESENT", handle
->handle
);
5153 else if(uData
->seen
)
5154 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5156 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5158 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5159 reply("CSMSG_USER_VACATION", handle
->handle
);
5164 static MODCMD_FUNC(cmd_names
)
5166 struct userNode
*targ
;
5167 struct userData
*targData
;
5168 unsigned int ii
, pos
;
5171 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5173 targ
= channel
->members
.list
[ii
]->user
;
5174 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5177 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5180 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5184 if(IsUserSuspended(targData
))
5186 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5189 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5190 reply("CSMSG_END_NAMES", channel
->name
);
5195 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5197 switch(ntype
->visible_type
)
5199 case NOTE_VIS_ALL
: return 1;
5200 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5201 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5206 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5208 struct userData
*uData
;
5210 switch(ntype
->set_access_type
)
5212 case NOTE_SET_CHANNEL_ACCESS
:
5213 if(!user
->handle_info
)
5215 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5217 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5218 case NOTE_SET_CHANNEL_SETTER
:
5219 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5220 case NOTE_SET_PRIVILEGED
: default:
5221 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5225 static CHANSERV_FUNC(cmd_note
)
5227 struct chanData
*cData
;
5229 struct note_type
*ntype
;
5231 cData
= channel
->channel_info
;
5234 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5238 /* If no arguments, show all visible notes for the channel. */
5244 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5246 note
= iter_data(it
);
5247 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5250 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5251 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5254 reply("CSMSG_NOTELIST_END", channel
->name
);
5256 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5258 /* If one argument, show the named note. */
5261 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5262 && note_type_visible_to_user(cData
, note
->type
, user
))
5264 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5266 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5267 && note_type_visible_to_user(NULL
, ntype
, user
))
5269 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5274 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5278 /* Assume they're trying to set a note. */
5282 ntype
= dict_find(note_types
, argv
[1], NULL
);
5285 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5288 else if(note_type_settable_by_user(channel
, ntype
, user
))
5290 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5291 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5292 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5293 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5294 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5296 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5298 /* The note is viewable to staff only, so return 0
5299 to keep the invocation from getting logged (or
5300 regular users can see it in !events). */
5306 reply("CSMSG_NO_ACCESS");
5313 static CHANSERV_FUNC(cmd_delnote
)
5318 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5319 || !note_type_settable_by_user(channel
, note
->type
, user
))
5321 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5324 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5325 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5329 static CHANSERV_FUNC(cmd_last
)
5335 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5337 if(numoflines
< 1 || numoflines
> 200)
5339 reply("CSMSG_LAST_INVALID");
5342 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5346 static CHANSERV_FUNC(cmd_events
)
5348 struct logSearch discrim
;
5349 struct logReport report
;
5350 unsigned int matches
, limit
;
5352 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5353 if(limit
< 1 || limit
> 200)
5356 memset(&discrim
, 0, sizeof(discrim
));
5357 discrim
.masks
.bot
= chanserv
;
5358 discrim
.masks
.channel_name
= channel
->name
;
5360 discrim
.masks
.command
= argv
[2];
5361 discrim
.limit
= limit
;
5362 discrim
.max_time
= INT_MAX
;
5363 discrim
.severities
= 1 << LOG_COMMAND
;
5364 report
.reporter
= chanserv
;
5366 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5367 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5369 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5371 reply("MSG_MATCH_COUNT", matches
);
5373 reply("MSG_NO_MATCHES");
5377 static CHANSERV_FUNC(cmd_say
)
5383 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5384 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5386 else if(GetUserH(argv
[1]))
5389 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5390 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5394 reply("MSG_NOT_TARGET_NAME");
5400 static CHANSERV_FUNC(cmd_emote
)
5406 /* CTCP is so annoying. */
5407 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5408 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5410 else if(GetUserH(argv
[1]))
5412 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5413 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5417 reply("MSG_NOT_TARGET_NAME");
5423 struct channelList
*
5424 chanserv_support_channels(void)
5426 return &chanserv_conf
.support_channels
;
5429 static CHANSERV_FUNC(cmd_expire
)
5431 int channel_count
= registered_channels
;
5432 expire_channels(NULL
);
5433 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5438 chanserv_expire_suspension(void *data
)
5440 struct suspended
*suspended
= data
;
5441 struct chanNode
*channel
;
5443 if(!suspended
->expires
|| (now
< suspended
->expires
))
5444 suspended
->revoked
= now
;
5445 channel
= suspended
->cData
->channel
;
5446 suspended
->cData
->channel
= channel
;
5447 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5448 if(!IsOffChannel(suspended
->cData
))
5450 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5451 ss_cs_join_channel(channel
, 1);
5455 static CHANSERV_FUNC(cmd_csuspend
)
5457 struct suspended
*suspended
;
5458 char reason
[MAXLEN
];
5459 time_t expiry
, duration
;
5460 struct userData
*uData
;
5464 if(IsProtected(channel
->channel_info
))
5466 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5470 if(argv
[1][0] == '!')
5472 else if(IsSuspended(channel
->channel_info
))
5474 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5475 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5479 if(!strcmp(argv
[1], "0"))
5481 else if((duration
= ParseInterval(argv
[1])))
5482 expiry
= now
+ duration
;
5485 reply("MSG_INVALID_DURATION", argv
[1]);
5489 unsplit_string(argv
+ 2, argc
- 2, reason
);
5491 suspended
= calloc(1, sizeof(*suspended
));
5492 suspended
->revoked
= 0;
5493 suspended
->issued
= now
;
5494 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5495 suspended
->expires
= expiry
;
5496 suspended
->reason
= strdup(reason
);
5497 suspended
->cData
= channel
->channel_info
;
5498 suspended
->previous
= suspended
->cData
->suspended
;
5499 suspended
->cData
->suspended
= suspended
;
5501 if(suspended
->expires
)
5502 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5504 if(IsSuspended(channel
->channel_info
))
5506 suspended
->previous
->revoked
= now
;
5507 if(suspended
->previous
->expires
)
5508 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5509 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5510 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5514 /* Mark all users in channel as absent. */
5515 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5524 /* Mark the channel as suspended, then part. */
5525 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5526 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5527 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5528 reply("CSMSG_SUSPENDED", channel
->name
);
5529 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5530 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5535 static CHANSERV_FUNC(cmd_cunsuspend
)
5537 struct suspended
*suspended
;
5538 char message
[MAXLEN
];
5540 if(!IsSuspended(channel
->channel_info
))
5542 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5546 suspended
= channel
->channel_info
->suspended
;
5548 /* Expire the suspension and join ChanServ to the channel. */
5549 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5550 chanserv_expire_suspension(suspended
);
5551 reply("CSMSG_UNSUSPENDED", channel
->name
);
5552 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5553 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5557 typedef struct chanservSearch
5565 unsigned long flags
;
5569 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5572 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5577 search
= malloc(sizeof(struct chanservSearch
));
5578 memset(search
, 0, sizeof(*search
));
5581 for(i
= 0; i
< argc
; i
++)
5583 /* Assume all criteria require arguments. */
5586 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5590 if(!irccasecmp(argv
[i
], "name"))
5591 search
->name
= argv
[++i
];
5592 else if(!irccasecmp(argv
[i
], "registrar"))
5593 search
->registrar
= argv
[++i
];
5594 else if(!irccasecmp(argv
[i
], "unvisited"))
5595 search
->unvisited
= ParseInterval(argv
[++i
]);
5596 else if(!irccasecmp(argv
[i
], "registered"))
5597 search
->registered
= ParseInterval(argv
[++i
]);
5598 else if(!irccasecmp(argv
[i
], "flags"))
5601 if(!irccasecmp(argv
[i
], "nodelete"))
5602 search
->flags
|= CHANNEL_NODELETE
;
5603 else if(!irccasecmp(argv
[i
], "suspended"))
5604 search
->flags
|= CHANNEL_SUSPENDED
;
5607 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5611 else if(!irccasecmp(argv
[i
], "limit"))
5612 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5615 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5620 if(search
->name
&& !strcmp(search
->name
, "*"))
5622 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5623 search
->registrar
= 0;
5632 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5634 const char *name
= channel
->channel
->name
;
5635 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5636 (search
->registrar
&& !channel
->registrar
) ||
5637 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5638 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5639 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5640 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5647 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5649 struct chanData
*channel
;
5650 unsigned int matches
= 0;
5652 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5654 if(!chanserv_channel_match(channel
, search
))
5664 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5669 search_print(struct chanData
*channel
, void *data
)
5671 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5674 static CHANSERV_FUNC(cmd_search
)
5677 unsigned int matches
;
5678 channel_search_func action
;
5682 if(!irccasecmp(argv
[1], "count"))
5683 action
= search_count
;
5684 else if(!irccasecmp(argv
[1], "print"))
5685 action
= search_print
;
5688 reply("CSMSG_ACTION_INVALID", argv
[1]);
5692 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5696 if(action
== search_count
)
5697 search
->limit
= INT_MAX
;
5699 if(action
== search_print
)
5701 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5702 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5706 matches
= chanserv_channel_search(search
, action
, user
);
5709 reply("MSG_MATCH_COUNT", matches
);
5711 reply("MSG_NO_MATCHES");
5717 static CHANSERV_FUNC(cmd_unvisited
)
5719 struct chanData
*cData
;
5720 time_t interval
= chanserv_conf
.channel_expire_delay
;
5721 char buffer
[INTERVALLEN
];
5722 unsigned int limit
= 25, matches
= 0;
5726 interval
= ParseInterval(argv
[1]);
5728 limit
= atoi(argv
[2]);
5731 intervalString(buffer
, interval
, user
->handle_info
);
5732 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5734 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5736 if((now
- cData
->visited
) < interval
)
5739 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5740 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5747 static MODCMD_FUNC(chan_opt_defaulttopic
)
5753 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5755 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5759 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5761 free(channel
->channel_info
->topic
);
5762 if(topic
[0] == '*' && topic
[1] == 0)
5764 topic
= channel
->channel_info
->topic
= NULL
;
5768 topic
= channel
->channel_info
->topic
= strdup(topic
);
5769 if(channel
->channel_info
->topic_mask
5770 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5771 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5773 SetChannelTopic(channel
, chanserv
, chanserv
, topic
? topic
: "", 1);
5776 if(channel
->channel_info
->topic
)
5777 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5779 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5783 static MODCMD_FUNC(chan_opt_topicmask
)
5787 struct chanData
*cData
= channel
->channel_info
;
5790 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5792 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5796 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5798 if(cData
->topic_mask
)
5799 free(cData
->topic_mask
);
5800 if(mask
[0] == '*' && mask
[1] == 0)
5802 cData
->topic_mask
= 0;
5806 cData
->topic_mask
= strdup(mask
);
5808 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5809 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5810 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5814 if(channel
->channel_info
->topic_mask
)
5815 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5817 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5821 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5825 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5829 if(greeting
[0] == '*' && greeting
[1] == 0)
5833 unsigned int length
= strlen(greeting
);
5834 if(length
> chanserv_conf
.greeting_length
)
5836 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5839 *data
= strdup(greeting
);
5848 reply(name
, user_find_message(user
, "MSG_NONE"));
5852 static MODCMD_FUNC(chan_opt_greeting
)
5854 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5857 static MODCMD_FUNC(chan_opt_usergreeting
)
5859 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5862 static MODCMD_FUNC(chan_opt_modes
)
5864 struct mod_chanmode
*new_modes
;
5865 char modes
[MODELEN
];
5869 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5871 reply("CSMSG_NO_ACCESS");
5874 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5876 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5878 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5880 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5883 else if(new_modes
->argc
> 1)
5885 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5886 mod_chanmode_free(new_modes
);
5891 channel
->channel_info
->modes
= *new_modes
;
5892 modcmd_chanmode_announce(new_modes
);
5893 mod_chanmode_free(new_modes
);
5897 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5899 reply("CSMSG_SET_MODES", modes
);
5901 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5905 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5907 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5909 struct chanData
*cData
= channel
->channel_info
;
5914 /* Set flag according to value. */
5915 if(enabled_string(argv
[1]))
5917 cData
->flags
|= mask
;
5920 else if(disabled_string(argv
[1]))
5922 cData
->flags
&= ~mask
;
5927 reply("MSG_INVALID_BINARY", argv
[1]);
5933 /* Find current option value. */
5934 value
= (cData
->flags
& mask
) ? 1 : 0;
5938 reply(name
, user_find_message(user
, "MSG_ON"));
5940 reply(name
, user_find_message(user
, "MSG_OFF"));
5944 static MODCMD_FUNC(chan_opt_nodelete
)
5946 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5948 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5952 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5955 static MODCMD_FUNC(chan_opt_dynlimit
)
5957 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5960 static MODCMD_FUNC(chan_opt_offchannel
)
5962 struct chanData
*cData
= channel
->channel_info
;
5967 /* Set flag according to value. */
5968 if(enabled_string(argv
[1]))
5970 if(!IsOffChannel(cData
))
5971 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5972 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5975 else if(disabled_string(argv
[1]))
5977 if(IsOffChannel(cData
))
5979 struct mod_chanmode change
;
5980 mod_chanmode_init(&change
);
5982 change
.args
[0].mode
= MODE_CHANOP
;
5983 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5984 mod_chanmode_announce(chanserv
, channel
, &change
);
5986 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5991 reply("MSG_INVALID_BINARY", argv
[1]);
5997 /* Find current option value. */
5998 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6002 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6004 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6008 static MODCMD_FUNC(chan_opt_defaults
)
6010 struct userData
*uData
;
6011 struct chanData
*cData
;
6012 const char *confirm
;
6013 enum levelOption lvlOpt
;
6014 enum charOption chOpt
;
6016 cData
= channel
->channel_info
;
6017 uData
= GetChannelUser(cData
, user
->handle_info
);
6018 if(!uData
|| (uData
->access
< UL_OWNER
))
6020 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6023 confirm
= make_confirmation_string(uData
);
6024 if((argc
< 2) || strcmp(argv
[1], confirm
))
6026 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6029 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6030 cData
->modes
= chanserv_conf
.default_modes
;
6031 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6032 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6033 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6034 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6035 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6040 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6042 struct chanData
*cData
= channel
->channel_info
;
6043 struct userData
*uData
;
6044 unsigned short value
;
6048 if(!check_user_level(channel
, user
, option
, 1, 1))
6050 reply("CSMSG_CANNOT_SET");
6053 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6054 if(!value
&& strcmp(argv
[1], "0"))
6056 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6059 uData
= GetChannelUser(cData
, user
->handle_info
);
6060 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6062 reply("CSMSG_BAD_SETLEVEL");
6068 /* This test only applies to owners, since non-owners
6069 * trying to set an option to above their level get caught
6070 * by the CSMSG_BAD_SETLEVEL test above.
6072 if(value
> uData
->access
)
6074 reply("CSMSG_BAD_SETTERS");
6081 cData
->lvlOpts
[option
] = value
;
6083 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6087 static MODCMD_FUNC(chan_opt_enfops
)
6089 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6092 static MODCMD_FUNC(chan_opt_enfhalfops
)
6094 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6096 static MODCMD_FUNC(chan_opt_enfmodes
)
6098 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6101 static MODCMD_FUNC(chan_opt_enftopic
)
6103 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6106 static MODCMD_FUNC(chan_opt_pubcmd
)
6108 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6111 static MODCMD_FUNC(chan_opt_setters
)
6113 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6116 static MODCMD_FUNC(chan_opt_userinfo
)
6118 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6121 static MODCMD_FUNC(chan_opt_topicsnarf
)
6123 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6126 static MODCMD_FUNC(chan_opt_inviteme
)
6128 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6131 /* TODO: Make look like this when no args are
6133 * -X3- -------------------------------
6134 * -X3- BanTimeout: Bans are removed:
6135 * -X3- ----- * indicates current -----
6136 * -X3- 0: [*] Never.
6137 * -X3- 1: [ ] After 10 minutes.
6138 * -X3- 2: [ ] After 2 hours.
6139 * -X3- 3: [ ] After 4 hours.
6140 * -X3- 4: [ ] After 24 hours.
6141 * -X3- 5: [ ] After one week.
6142 * -X3- ------------- End -------------
6145 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6147 struct chanData
*cData
= channel
->channel_info
;
6148 int count
= charOptions
[option
].count
, index
;
6152 index
= atoi(argv
[1]);
6154 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6156 reply("CSMSG_INVALID_NUMERIC", index
);
6157 /* Show possible values. */
6158 for(index
= 0; index
< count
; index
++)
6159 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6163 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6167 /* Find current option value. */
6170 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6174 /* Somehow, the option value is corrupt; reset it to the default. */
6175 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6180 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6184 static MODCMD_FUNC(chan_opt_automode
)
6186 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6189 static MODCMD_FUNC(chan_opt_protect
)
6191 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6194 static MODCMD_FUNC(chan_opt_toys
)
6196 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6199 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6201 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6204 static MODCMD_FUNC(chan_opt_bantimeout
)
6206 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6209 static MODCMD_FUNC(chan_opt_topicrefresh
)
6211 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6214 static MODCMD_FUNC(chan_opt_resync
)
6216 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6219 static MODCMD_FUNC(chan_opt_bantype
)
6221 return channel_multiple_option(chBanType
, CSFUNC_ARGS
);
6224 static struct svccmd_list set_shows_list
;
6227 handle_svccmd_unbind(struct svccmd
*target
) {
6229 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6230 if(target
== set_shows_list
.list
[ii
])
6231 set_shows_list
.used
= 0;
6234 static CHANSERV_FUNC(cmd_set
)
6236 struct svccmd
*subcmd
;
6240 /* Check if we need to (re-)initialize set_shows_list. */
6241 if(!set_shows_list
.used
)
6243 if(!set_shows_list
.size
)
6245 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6246 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6248 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6250 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6251 sprintf(buf
, "%s %s", argv
[0], name
);
6252 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6255 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6258 svccmd_list_append(&set_shows_list
, subcmd
);
6264 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6265 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6267 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6269 subcmd
= set_shows_list
.list
[ii
];
6270 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6272 reply("CSMSG_CHANNEL_OPTIONS_END");
6276 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6277 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6280 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6283 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6285 reply("CSMSG_NO_ACCESS");
6289 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6293 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6295 struct userData
*uData
;
6297 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6300 reply("CSMSG_NOT_USER", channel
->name
);
6306 /* Just show current option value. */
6308 else if(enabled_string(argv
[1]))
6310 uData
->flags
|= mask
;
6312 else if(disabled_string(argv
[1]))
6314 uData
->flags
&= ~mask
;
6318 reply("MSG_INVALID_BINARY", argv
[1]);
6322 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6326 static MODCMD_FUNC(user_opt_autoop
)
6328 struct userData
*uData
;
6330 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6333 reply("CSMSG_NOT_USER", channel
->name
);
6336 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6337 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6339 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6340 /* TODO: add halfops error message? or is the op one generic enough? */
6343 static MODCMD_FUNC(user_opt_autoinvite
)
6345 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6348 static MODCMD_FUNC(user_opt_info
)
6350 struct userData
*uData
;
6353 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6357 /* If they got past the command restrictions (which require access)
6358 * but fail this test, we have some fool with security override on.
6360 reply("CSMSG_NOT_USER", channel
->name
);
6367 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6368 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6370 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6373 bp
= strcspn(infoline
, "\001");
6376 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6381 if(infoline
[0] == '*' && infoline
[1] == 0)
6384 uData
->info
= strdup(infoline
);
6387 reply("CSMSG_USET_INFO", uData
->info
);
6389 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6393 struct svccmd_list uset_shows_list
;
6395 static CHANSERV_FUNC(cmd_uset
)
6397 struct svccmd
*subcmd
;
6401 /* Check if we need to (re-)initialize uset_shows_list. */
6402 if(!uset_shows_list
.used
)
6406 "AutoOp", "AutoInvite", "Info"
6409 if(!uset_shows_list
.size
)
6411 uset_shows_list
.size
= ArrayLength(options
);
6412 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6414 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6416 const char *name
= options
[ii
];
6417 sprintf(buf
, "%s %s", argv
[0], name
);
6418 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6421 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6424 svccmd_list_append(&uset_shows_list
, subcmd
);
6430 /* Do this so options are presented in a consistent order. */
6431 reply("CSMSG_USER_OPTIONS");
6432 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6433 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6437 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6438 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6441 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6445 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6448 static CHANSERV_FUNC(cmd_giveownership
)
6450 struct handle_info
*new_owner_hi
;
6451 struct userData
*new_owner
, *curr_user
;
6452 struct chanData
*cData
= channel
->channel_info
;
6453 struct do_not_register
*dnr
;
6454 struct giveownership
*giveownership
;
6455 unsigned int force
, override
;
6456 unsigned short co_access
, new_owner_old_access
;
6457 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6460 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6461 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6463 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6464 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6465 && (uData
->access
> 500)
6466 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6467 || uData
->access
< 500));
6470 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6472 struct userData
*owner
= NULL
;
6473 for(curr_user
= channel
->channel_info
->users
;
6475 curr_user
= curr_user
->next
)
6477 if(curr_user
->access
!= UL_OWNER
)
6481 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6488 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6490 char delay
[INTERVALLEN
];
6491 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6492 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6495 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6497 if(new_owner_hi
== user
->handle_info
)
6499 reply("CSMSG_NO_TRANSFER_SELF");
6502 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6507 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6511 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6515 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6517 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6520 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6521 if(!IsHelping(user
))
6522 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6524 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6528 new_owner_old_access
= new_owner
->access
;
6529 if(new_owner
->access
>= UL_COOWNER
)
6530 co_access
= new_owner
->access
;
6532 co_access
= UL_COOWNER
;
6533 new_owner
->access
= UL_OWNER
;
6535 curr_user
->access
= co_access
;
6536 cData
->ownerTransfer
= now
;
6538 giveownership
= calloc(1, sizeof(*giveownership
));
6539 giveownership
->issued
= now
;
6540 giveownership
->old_owner
= curr_user
->handle
->handle
;
6541 giveownership
->target
= new_owner_hi
->handle
;
6542 giveownership
->target_access
= new_owner_old_access
;
6545 if(argc
> (2 + force
))
6547 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6548 giveownership
->reason
= strdup(transfer_reason
);
6550 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6553 giveownership
->previous
= channel
->channel_info
->giveownership
;
6554 channel
->channel_info
->giveownership
= giveownership
;
6556 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6557 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6558 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6563 chanserv_expire_user_suspension(void *data
)
6565 struct userData
*target
= data
;
6567 target
->expires
= 0;
6568 target
->flags
&= ~USER_SUSPENDED
;
6571 static CHANSERV_FUNC(cmd_suspend
)
6573 struct handle_info
*hi
;
6574 struct userData
*self
, *target
;
6578 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6579 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6580 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6582 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6585 if(target
->access
>= self
->access
)
6587 reply("MSG_USER_OUTRANKED", hi
->handle
);
6590 if(target
->flags
& USER_SUSPENDED
)
6592 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6597 target
->present
= 0;
6600 if(!strcmp(argv
[2], "0"))
6604 unsigned int duration
;
6605 if(!(duration
= ParseInterval(argv
[2])))
6607 reply("MSG_INVALID_DURATION", argv
[2]);
6610 expiry
= now
+ duration
;
6613 target
->expires
= expiry
;
6616 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6618 target
->flags
|= USER_SUSPENDED
;
6619 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6623 static CHANSERV_FUNC(cmd_unsuspend
)
6625 struct handle_info
*hi
;
6626 struct userData
*self
, *target
;
6629 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6630 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6631 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6633 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6636 if(target
->access
>= self
->access
)
6638 reply("MSG_USER_OUTRANKED", hi
->handle
);
6641 if(!(target
->flags
& USER_SUSPENDED
))
6643 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6646 target
->flags
&= ~USER_SUSPENDED
;
6647 scan_user_presence(target
, NULL
);
6648 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6649 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6653 static MODCMD_FUNC(cmd_deleteme
)
6655 struct handle_info
*hi
;
6656 struct userData
*target
;
6657 const char *confirm_string
;
6658 unsigned short access
;
6661 hi
= user
->handle_info
;
6662 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6664 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6667 if(target
->access
== UL_OWNER
)
6669 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6672 confirm_string
= make_confirmation_string(target
);
6673 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6675 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6678 access
= target
->access
;
6679 channel_name
= strdup(channel
->name
);
6680 del_channel_user(target
, 1);
6681 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6687 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6689 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6690 struct chanData
*cData
;
6693 for(cData
= channelList
; cData
; cData
= cData
->next
)
6695 if(IsSuspended(cData
))
6697 opt
= cData
->chOpts
[chTopicRefresh
];
6700 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6703 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6704 cData
->last_refresh
= refresh_num
;
6706 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6710 chanserv_auto_resync(UNUSED_ARG(void *data
))
6712 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6713 struct chanData
*cData
;
6716 for(cData
= channelList
; cData
; cData
= cData
->next
)
6718 if(IsSuspended(cData
)) continue;
6719 opt
= cData
->chOpts
[chResync
];
6720 if(opt
== 'n') continue;
6721 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6722 resync_channel(cData
->channel
);
6723 cData
->last_resync
= refresh_num
;
6725 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6728 static CHANSERV_FUNC(cmd_unf
)
6732 char response
[MAXLEN
];
6733 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6734 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6735 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6738 reply("CSMSG_UNF_RESPONSE");
6742 static CHANSERV_FUNC(cmd_ping
)
6746 char response
[MAXLEN
];
6747 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6748 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6749 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6752 reply("CSMSG_PING_RESPONSE");
6756 static CHANSERV_FUNC(cmd_wut
)
6760 char response
[MAXLEN
];
6761 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6762 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6763 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6766 reply("CSMSG_WUT_RESPONSE");
6771 static CHANSERV_FUNC(cmd_8ball
)
6773 unsigned int i
, j
, accum
;
6778 for(i
=1; i
<argc
; i
++)
6779 for(j
=0; argv
[i
][j
]; j
++)
6780 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6781 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6784 char response
[MAXLEN
];
6785 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6786 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6789 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6793 #else /* Use cool 8ball instead */
6795 void eightball(char *outcome
, int method
, unsigned int seed
)
6799 #define NUMOFCOLORS 18
6800 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6801 "white", "black", "grey", "brown",
6802 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6803 "fuchsia","turquoise","magenta", "cyan"};
6804 #define NUMOFLOCATIONS 50
6805 char balllocations
[50][55] = {
6806 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6807 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6808 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6809 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6810 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6811 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6812 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6813 "your bra", "your hair", "your bed", "the couch", "the wall",
6814 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6815 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6816 #define NUMOFPREPS 15
6817 char ballpreps
[50][50] = {
6818 "Near", "Somewhere near", "In", "In", "In",
6819 "In", "Hiding in", "Under", "Next to", "Over",
6820 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6821 #define NUMOFNUMS 34
6822 char ballnums
[50][50] = {
6823 "A hundred", "A thousand", "A few", "42",
6824 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6825 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6826 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6828 #define NUMOFMULTS 8
6829 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6832 * 0: normal (Not used in x3)
6839 if (method
== 1) /* A Color */
6843 answer
= (rand() % 12); /* Make sure this is the # of entries */
6846 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6848 case 1: strcpy(tmp
, "Sort of a light %s color.");
6850 case 2: strcpy(tmp
, "Dark and dreary %s.");
6852 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6854 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6856 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6858 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6860 case 10: strcpy(tmp
, "Solid %s.");
6862 case 11: strcpy(tmp
, "Transparent %s.");
6864 default: strcpy(outcome
, "An invalid random number was generated.");
6867 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6870 else if (method
== 2) /* Location */
6872 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6874 else if (method
== 3) /* Number of ___ */
6876 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6880 //Debug(DBGWARNING, "Error in 8ball.");
6885 static CHANSERV_FUNC(cmd_8ball
)
6887 char *word1
, *word2
, *word3
;
6888 static char eb
[MAXLEN
];
6889 unsigned int accum
, i
, j
;
6893 for(i
=1; i
<argc
; i
++)
6894 for(j
=0; argv
[i
][j
]; j
++)
6895 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6897 accum
+= time(NULL
)/3600;
6899 word2
= argc
>2?argv
[2]:"";
6900 word3
= argc
>3?argv
[3]:"";
6903 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6904 eightball(eb
, 1, accum
);
6905 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6906 eightball(eb
, 1, accum
);
6907 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6908 eightball(eb
, 1, accum
);
6909 /*** LOCATION *****/
6914 (strcasecmp(word1
, "where") == 0) &&
6915 (strcasecmp(word2
, "is") == 0)
6919 strcasecmp(word1
, "where's") == 0
6922 eightball(eb
, 2, accum
);
6924 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6925 eightball(eb
, 3, accum
);
6929 /* Generic 8ball question.. so pull from x3.conf srvx style */
6932 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6935 char response
[MAXLEN
];
6936 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6937 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6940 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6946 char response
[MAXLEN
];
6947 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6948 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6951 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6956 static CHANSERV_FUNC(cmd_d
)
6958 unsigned long sides
, count
, modifier
, ii
, total
;
6959 char response
[MAXLEN
], *sep
;
6963 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6973 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6974 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6978 else if((sep
[0] == '-') && isdigit(sep
[1]))
6979 modifier
= strtoul(sep
, NULL
, 10);
6980 else if((sep
[0] == '+') && isdigit(sep
[1]))
6981 modifier
= strtoul(sep
+1, NULL
, 10);
6988 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6993 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6996 for(total
= ii
= 0; ii
< count
; ++ii
)
6997 total
+= (rand() % sides
) + 1;
7000 if((count
> 1) || modifier
)
7002 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7003 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7007 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7008 sprintf(response
, fmt
, total
, sides
);
7011 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7013 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7017 static CHANSERV_FUNC(cmd_huggle
)
7019 /* CTCP must be via PRIVMSG, never notice */
7021 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7023 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7027 static CHANSERV_FUNC(cmd_calc
)
7029 char response
[MAXLEN
];
7032 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7035 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7037 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7042 chanserv_adjust_limit(void *data
)
7044 struct mod_chanmode change
;
7045 struct chanData
*cData
= data
;
7046 struct chanNode
*channel
= cData
->channel
;
7049 if(IsSuspended(cData
))
7052 cData
->limitAdjusted
= now
;
7053 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7054 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7056 if(limit
> cData
->modes
.new_limit
)
7057 limit
= cData
->modes
.new_limit
;
7058 else if(limit
== cData
->modes
.new_limit
)
7062 mod_chanmode_init(&change
);
7063 change
.modes_set
= MODE_LIMIT
;
7064 change
.new_limit
= limit
;
7065 mod_chanmode_announce(chanserv
, channel
, &change
);
7069 handle_new_channel(struct chanNode
*channel
)
7071 struct chanData
*cData
;
7073 if(!(cData
= channel
->channel_info
))
7076 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7077 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7079 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7080 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7083 /* Welcome to my worst nightmare. Warning: Read (or modify)
7084 the code below at your own risk. */
7086 handle_join(struct modeNode
*mNode
)
7088 struct mod_chanmode change
;
7089 struct userNode
*user
= mNode
->user
;
7090 struct chanNode
*channel
= mNode
->channel
;
7091 struct chanData
*cData
;
7092 struct userData
*uData
= NULL
;
7093 struct banData
*bData
;
7094 struct handle_info
*handle
;
7095 unsigned int modes
= 0, info
= 0;
7098 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7101 cData
= channel
->channel_info
;
7102 if(channel
->members
.used
> cData
->max
)
7103 cData
->max
= channel
->members
.used
;
7106 /* Check for bans. If they're joining through a ban, one of two
7108 * 1: Join during a netburst, by riding the break. Kick them
7109 * unless they have ops or voice in the channel.
7110 * 2: They're allowed to join through the ban (an invite in
7111 * ircu2.10, or a +e on Hybrid, or something).
7112 * If they're not joining through a ban, and the banlist is not
7113 * full, see if they're on the banlist for the channel. If so,
7116 if(user
->uplink
->burst
&& !mNode
->modes
)
7119 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7121 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7123 /* Riding a netburst. Naughty. */
7124 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7131 mod_chanmode_init(&change
);
7133 if(channel
->banlist
.used
< MAXBANS
)
7135 /* Not joining through a ban. */
7136 for(bData
= cData
->bans
;
7137 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7138 bData
= bData
->next
);
7142 char kick_reason
[MAXLEN
];
7143 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7145 bData
->triggered
= now
;
7146 if(bData
!= cData
->bans
)
7148 /* Shuffle the ban to the head of the list. */
7150 bData
->next
->prev
= bData
->prev
;
7152 bData
->prev
->next
= bData
->next
;
7155 bData
->next
= cData
->bans
;
7158 cData
->bans
->prev
= bData
;
7159 cData
->bans
= bData
;
7162 change
.args
[0].mode
= MODE_BAN
;
7163 change
.args
[0].u
.hostmask
= bData
->mask
;
7164 mod_chanmode_announce(chanserv
, channel
, &change
);
7165 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7170 /* ChanServ will not modify the limits in join-flooded channels.
7171 It will also skip DynLimit processing when the user (or srvx)
7172 is bursting in, because there are likely more incoming. */
7173 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7174 && !user
->uplink
->burst
7175 && !channel
->join_flooded
7176 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7178 /* The user count has begun "bumping" into the channel limit,
7179 so set a timer to raise the limit a bit. Any previous
7180 timers are removed so three incoming users within the delay
7181 results in one limit change, not three. */
7183 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7184 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7187 /* Give automodes exept during join-floods */
7188 if(!channel
->join_flooded
)
7190 if(cData
->chOpts
[chAutomode
] == 'v')
7191 modes
|= MODE_VOICE
;
7192 else if(cData
->chOpts
[chAutomode
] == 'h')
7193 modes
|= MODE_HALFOP
;
7194 else if(cData
->chOpts
[chAutomode
] == 'o')
7195 modes
|= MODE_CHANOP
;
7198 greeting
= cData
->greeting
;
7199 if(user
->handle_info
)
7201 handle
= user
->handle_info
;
7203 if(IsHelper(user
) && !IsHelping(user
))
7206 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7208 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7210 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7216 uData
= GetTrueChannelAccess(cData
, handle
);
7217 if(uData
&& !IsUserSuspended(uData
))
7219 /* non users getting automodes are handled above. */
7220 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7222 if(uData
->access
>= UL_OP
)
7223 modes
|= MODE_CHANOP
;
7224 else if(uData
->access
>= UL_HALFOP
)
7225 modes
|= MODE_HALFOP
;
7226 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7227 modes
|= MODE_VOICE
;
7229 if(uData
->access
>= UL_PRESENT
)
7230 cData
->visited
= now
;
7231 if(cData
->user_greeting
)
7232 greeting
= cData
->user_greeting
;
7234 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7235 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7243 /* If user joining normally (not during burst), apply op or voice,
7244 * and send greeting/userinfo as appropriate.
7246 if(!user
->uplink
->burst
)
7250 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7251 if(modes & MODE_CHANOP) {
7252 modes &= ~MODE_HALFOP;
7253 modes &= ~MODE_VOICE;
7256 change
.args
[0].mode
= modes
;
7257 change
.args
[0].u
.member
= mNode
;
7258 mod_chanmode_announce(chanserv
, channel
, &change
);
7261 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7263 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7269 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7271 struct mod_chanmode change
;
7272 struct userData
*channel
;
7273 unsigned int ii
, jj
, i
;
7275 if(!user
->handle_info
)
7278 mod_chanmode_init(&change
);
7280 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7282 struct chanNode
*cn
;
7283 struct modeNode
*mn
;
7284 if(IsUserSuspended(channel
)
7285 || IsSuspended(channel
->channel
)
7286 || !(cn
= channel
->channel
->channel
))
7289 mn
= GetUserMode(cn
, user
);
7292 if(!IsUserSuspended(channel
)
7293 && IsUserAutoInvite(channel
)
7294 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7296 && !user
->uplink
->burst
)
7297 irc_invite(chanserv
, user
, cn
);
7301 if(channel
->access
>= UL_PRESENT
)
7302 channel
->channel
->visited
= now
;
7304 if(IsUserAutoOp(channel
))
7306 if(channel
->access
>= UL_OP
)
7307 change
.args
[0].mode
= MODE_CHANOP
;
7308 else if(channel
->access
>= UL_HALFOP
)
7309 change
.args
[0].mode
= MODE_HALFOP
;
7310 else if(channel
->access
>= UL_PEON
)
7311 change
.args
[0].mode
= MODE_VOICE
;
7313 change
.args
[0].mode
= 0;
7314 change
.args
[0].u
.member
= mn
;
7315 if(change
.args
[0].mode
)
7316 mod_chanmode_announce(chanserv
, cn
, &change
);
7319 channel
->seen
= now
;
7320 channel
->present
= 1;
7323 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7325 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7326 struct banData
*ban
;
7328 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7329 || !channel
->channel_info
7330 || IsSuspended(channel
->channel_info
))
7332 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7333 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7335 if(jj
< channel
->banlist
.used
)
7337 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7339 char kick_reason
[MAXLEN
];
7340 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7342 change
.args
[0].mode
= MODE_BAN
;
7343 change
.args
[0].u
.hostmask
= ban
->mask
;
7344 mod_chanmode_announce(chanserv
, channel
, &change
);
7345 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7346 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7347 ban
->triggered
= now
;
7352 if(IsSupportHelper(user
))
7354 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7356 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7358 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7364 if (user
->handle_info
->ignores
->used
) {
7365 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7366 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7370 if (user
->handle_info
->epithet
)
7371 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7375 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7377 struct chanData
*cData
;
7378 struct userData
*uData
;
7380 cData
= mn
->channel
->channel_info
;
7381 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7384 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7386 /* Allow for a bit of padding so that the limit doesn't
7387 track the user count exactly, which could get annoying. */
7388 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7390 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7391 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7395 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7397 scan_user_presence(uData
, mn
->user
);
7401 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7403 unsigned int ii
, jj
;
7404 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7406 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7407 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7409 if(jj
< mn
->user
->channels
.used
)
7412 if(ii
== chanserv_conf
.support_channels
.used
)
7413 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7418 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7420 struct userData
*uData
;
7422 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7423 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7424 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7427 if(protect_user(victim
, kicker
, channel
->channel_info
))
7429 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7430 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7433 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7438 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7440 struct chanData
*cData
;
7442 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7445 cData
= channel
->channel_info
;
7446 if(bad_topic(channel
, user
, channel
->topic
))
7447 { /* User doesnt have privs to set topics. Undo it */
7448 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7449 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7452 /* If there is a topic mask set, and the new topic doesnt match,
7453 * set the topic to mask + new_topic */
7454 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7456 char new_topic
[TOPICLEN
+1];
7457 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7460 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7461 /* and fall through to topicsnarf code below.. */
7463 else /* Topic couldnt fit into mask, was too long */
7465 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7466 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7467 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7471 /* With topicsnarf, grab the topic and save it as the default topic. */
7472 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7475 cData
->topic
= strdup(channel
->topic
);
7481 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7483 struct mod_chanmode
*bounce
= NULL
;
7484 unsigned int bnc
, ii
;
7487 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7490 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7491 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7493 char correct
[MAXLEN
];
7494 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7495 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7496 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7498 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7500 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7502 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7503 if(!protect_user(victim
, user
, channel
->channel_info
))
7506 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7509 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7510 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7511 if(bounce
->args
[bnc
].u
.member
)
7515 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7516 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7518 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7520 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7522 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7523 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7526 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7527 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7528 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7531 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7533 const char *ban
= change
->args
[ii
].u
.hostmask
;
7534 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7537 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7538 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7539 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7541 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7546 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7547 mod_chanmode_announce(chanserv
, channel
, bounce
);
7548 for(ii
= 0; ii
< change
->argc
; ++ii
)
7549 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7550 free((char*)bounce
->args
[ii
].u
.hostmask
);
7551 mod_chanmode_free(bounce
);
7556 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7558 struct chanNode
*channel
;
7559 struct banData
*bData
;
7560 struct mod_chanmode change
;
7561 unsigned int ii
, jj
;
7562 char kick_reason
[MAXLEN
];
7564 mod_chanmode_init(&change
);
7566 change
.args
[0].mode
= MODE_BAN
;
7567 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7569 channel
= user
->channels
.list
[ii
]->channel
;
7570 /* Need not check for bans if they're opped or voiced. */
7571 /* TODO: does this make sense in automode v, h, and o? *
7572 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7573 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7575 /* Need not check for bans unless channel registration is active. */
7576 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7578 /* Look for a matching ban already on the channel. */
7579 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7580 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7582 /* Need not act if we found one. */
7583 if(jj
< channel
->banlist
.used
)
7585 /* Look for a matching ban in this channel. */
7586 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7588 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7590 change
.args
[0].u
.hostmask
= bData
->mask
;
7591 mod_chanmode_announce(chanserv
, channel
, &change
);
7592 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7593 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7594 bData
->triggered
= now
;
7595 break; /* we don't need to check any more bans in the channel */
7600 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7602 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7606 dict_remove2(handle_dnrs
, old_handle
, 1);
7607 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7608 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7613 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7615 struct userNode
*h_user
;
7617 if(handle
->channels
)
7619 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7620 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7622 while(handle
->channels
)
7623 del_channel_user(handle
->channels
, 1);
7628 handle_server_link(UNUSED_ARG(struct server
*server
))
7630 struct chanData
*cData
;
7632 for(cData
= channelList
; cData
; cData
= cData
->next
)
7634 if(!IsSuspended(cData
))
7635 cData
->may_opchan
= 1;
7636 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7637 && !cData
->channel
->join_flooded
7638 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7639 < chanserv_conf
.adjust_threshold
))
7641 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7642 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7648 chanserv_conf_read(void)
7652 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7653 struct mod_chanmode
*change
;
7654 struct string_list
*strlist
;
7655 struct chanNode
*chan
;
7658 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7660 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7663 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7664 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7665 chanserv_conf
.support_channels
.used
= 0;
7666 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7668 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7670 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7673 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7675 channelList_append(&chanserv_conf
.support_channels
, chan
);
7678 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7681 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7684 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7686 channelList_append(&chanserv_conf
.support_channels
, chan
);
7688 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7689 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7690 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7691 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7692 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7693 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7694 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7695 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7696 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7697 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7698 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7699 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7700 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7701 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7702 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7703 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7704 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7705 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7706 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7707 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7708 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7709 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7710 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7711 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7712 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7714 NickChange(chanserv
, str
, 0);
7715 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7716 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7717 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7718 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7719 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7720 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7721 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7722 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7723 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7724 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7725 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7726 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7727 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7728 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7729 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7730 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7731 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7732 god_timeout
= str
? ParseInterval(str
) : 60*15;
7733 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7736 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7737 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7738 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7739 && (change
->argc
< 2))
7741 chanserv_conf
.default_modes
= *change
;
7742 mod_chanmode_free(change
);
7744 free_string_list(chanserv_conf
.set_shows
);
7745 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7747 strlist
= string_list_copy(strlist
);
7750 static const char *list
[] = {
7751 /* free form text */
7752 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7753 /* options based on user level */
7754 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7755 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7756 /* multiple choice options */
7757 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7759 /* binary options */
7760 "DynLimit", "NoDelete", "BanTimeout",
7765 strlist
= alloc_string_list(ArrayLength(list
)-1);
7766 for(ii
=0; list
[ii
]; ii
++)
7767 string_list_append(strlist
, strdup(list
[ii
]));
7769 chanserv_conf
.set_shows
= strlist
;
7770 /* We don't look things up now, in case the list refers to options
7771 * defined by modules initialized after this point. Just mark the
7772 * function list as invalid, so it will be initialized.
7774 set_shows_list
.used
= 0;
7775 free_string_list(chanserv_conf
.eightball
);
7776 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7779 strlist
= string_list_copy(strlist
);
7783 strlist
= alloc_string_list(4);
7784 string_list_append(strlist
, strdup("Yes."));
7785 string_list_append(strlist
, strdup("No."));
7786 string_list_append(strlist
, strdup("Maybe so."));
7788 chanserv_conf
.eightball
= strlist
;
7789 free_string_list(chanserv_conf
.old_ban_names
);
7790 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7792 strlist
= string_list_copy(strlist
);
7794 strlist
= alloc_string_list(2);
7795 chanserv_conf
.old_ban_names
= strlist
;
7796 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7797 off_channel
= str
? atoi(str
) : 0;
7801 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7804 struct note_type
*ntype
;
7807 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7809 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7812 if(!(ntype
= chanserv_create_note_type(key
)))
7814 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7818 /* Figure out set access */
7819 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7821 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7822 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7824 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7826 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7827 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7829 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7831 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7835 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7836 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7837 ntype
->set_access
.min_opserv
= 0;
7840 /* Figure out visibility */
7841 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7842 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7843 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7844 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7845 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7846 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7847 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7848 ntype
->visible_type
= NOTE_VIS_ALL
;
7850 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7852 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7853 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7857 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7859 struct handle_info
*handle
;
7860 struct userData
*uData
;
7861 char *seen
, *inf
, *flags
, *expires
;
7863 unsigned short access
;
7865 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7867 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7871 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7872 if(access
> UL_OWNER
)
7874 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7878 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7879 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7880 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7881 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7882 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7883 handle
= get_handle_info(key
);
7886 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7890 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7891 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7892 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7894 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7896 if(uData
->expires
> now
)
7897 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7899 uData
->flags
&= ~USER_SUSPENDED
;
7902 /* Upgrade: set autoop to the inverse of noautoop */
7903 if(chanserv_read_version
< 2)
7905 /* if noautoop is true, set autoop false, and vice versa */
7906 if(uData
->flags
& USER_NOAUTO_OP
)
7907 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7909 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7910 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
);
7916 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7918 struct banData
*bData
;
7919 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7920 time_t set_time
, triggered_time
, expires_time
;
7922 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7924 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7928 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7929 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7930 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7931 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7932 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7933 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7934 if (!reason
|| !owner
)
7937 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7938 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7940 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7942 expires_time
= set_time
+ atoi(s_duration
);
7946 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7949 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7952 static struct suspended
*
7953 chanserv_read_suspended(dict_t obj
)
7955 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7959 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7960 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7961 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7962 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7963 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7964 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7965 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7966 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7967 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7968 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7972 static struct giveownership
*
7973 chanserv_read_giveownership(dict_t obj
)
7975 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7979 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7980 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7982 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7984 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7985 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7987 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7988 giveownership
->reason
= str
? strdup(str
) : NULL
;
7989 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7990 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7992 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7993 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7994 return giveownership
;
7998 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8000 struct suspended
*suspended
;
8001 struct giveownership
*giveownership
;
8002 struct mod_chanmode
*modes
;
8003 struct chanNode
*cNode
;
8004 struct chanData
*cData
;
8005 struct dict
*channel
, *obj
;
8006 char *str
, *argv
[10];
8010 channel
= hir
->d
.object
;
8012 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8015 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8018 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8021 cData
= register_channel(cNode
, str
);
8024 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8028 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8030 enum levelOption lvlOpt
;
8031 enum charOption chOpt
;
8033 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8034 cData
->flags
= atoi(str
);
8036 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8038 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8040 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8041 else if(levelOptions
[lvlOpt
].old_flag
)
8043 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8044 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8046 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8050 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8052 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8054 cData
->chOpts
[chOpt
] = str
[0];
8057 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8059 enum levelOption lvlOpt
;
8060 enum charOption chOpt
;
8063 cData
->flags
= base64toint(str
, 5);
8064 count
= strlen(str
+= 5);
8065 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8068 if(levelOptions
[lvlOpt
].old_flag
)
8070 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8071 lvl
= levelOptions
[lvlOpt
].flag_value
;
8073 lvl
= levelOptions
[lvlOpt
].default_value
;
8075 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8077 case 'c': lvl
= UL_COOWNER
; break;
8078 case 'm': lvl
= UL_MANAGER
; break;
8079 case 'n': lvl
= UL_OWNER
+1; break;
8080 case 'o': lvl
= UL_OP
; break;
8081 case 'p': lvl
= UL_PEON
; break;
8082 case 'h': lvl
= UL_HALFOP
; break;
8083 case 'w': lvl
= UL_OWNER
; break;
8084 default: lvl
= 0; break;
8086 cData
->lvlOpts
[lvlOpt
] = lvl
;
8088 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8089 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8092 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8094 suspended
= chanserv_read_suspended(obj
);
8095 cData
->suspended
= suspended
;
8096 suspended
->cData
= cData
;
8097 /* We could use suspended->expires and suspended->revoked to
8098 * set the CHANNEL_SUSPENDED flag, but we don't. */
8100 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8102 suspended
= calloc(1, sizeof(*suspended
));
8103 suspended
->issued
= 0;
8104 suspended
->revoked
= 0;
8105 suspended
->suspender
= strdup(str
);
8106 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8107 suspended
->expires
= str
? atoi(str
) : 0;
8108 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8109 suspended
->reason
= strdup(str
? str
: "No reason");
8110 suspended
->previous
= NULL
;
8111 cData
->suspended
= suspended
;
8112 suspended
->cData
= cData
;
8116 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8117 suspended
= NULL
; /* to squelch a warning */
8120 if(IsSuspended(cData
)) {
8121 if(suspended
->expires
> now
)
8122 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8123 else if(suspended
->expires
)
8124 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8127 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8129 giveownership
= chanserv_read_giveownership(obj
);
8130 cData
->giveownership
= giveownership
;
8133 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8134 struct mod_chanmode change
;
8135 mod_chanmode_init(&change
);
8137 change
.args
[0].mode
= MODE_CHANOP
;
8138 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8139 mod_chanmode_announce(chanserv
, cNode
, &change
);
8142 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8143 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8144 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8145 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8146 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8147 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8148 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8149 cData
->max
= str
? atoi(str
) : 0;
8150 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8151 cData
->greeting
= str
? strdup(str
) : NULL
;
8152 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8153 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8154 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8155 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8156 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8157 cData
->topic
= str
? strdup(str
) : NULL
;
8159 if(!IsSuspended(cData
)
8160 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8161 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8162 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8163 cData
->modes
= *modes
;
8165 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8166 if(cData
->modes
.argc
> 1)
8167 cData
->modes
.argc
= 1;
8168 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8169 mod_chanmode_free(modes
);
8172 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8173 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8174 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8176 if(!cData
->users
&& !IsProtected(cData
))
8178 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8179 unregister_channel(cData
, "has empty user list.");
8183 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8184 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8185 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8187 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8188 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8190 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8191 struct record_data
*rd
= iter_data(it
);
8192 const char *note
, *setter
;
8194 if(rd
->type
!= RECDB_OBJECT
)
8196 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8200 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8202 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8204 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8208 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8209 if(!setter
) setter
= "<unknown>";
8210 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8218 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8220 const char *setter
, *reason
, *str
;
8221 struct do_not_register
*dnr
;
8223 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8226 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8229 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8232 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8235 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8238 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8240 dnr
->set
= atoi(str
);
8246 chanserv_version_read(struct dict
*section
)
8250 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8252 chanserv_read_version
= atoi(str
);
8253 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8257 chanserv_saxdb_read(struct dict
*database
)
8259 struct dict
*section
;
8262 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8263 chanserv_version_read(section
);
8265 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8266 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8267 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8269 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8270 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8271 chanserv_channel_read(iter_key(it
), iter_data(it
));
8273 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8274 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8275 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8281 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8283 int high_present
= 0;
8284 saxdb_start_record(ctx
, KEY_USERS
, 1);
8285 for(; uData
; uData
= uData
->next
)
8287 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8289 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8290 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8291 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8293 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8295 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8297 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8298 saxdb_end_record(ctx
);
8300 saxdb_end_record(ctx
);
8301 return high_present
;
8305 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8309 saxdb_start_record(ctx
, KEY_BANS
, 1);
8310 for(; bData
; bData
= bData
->next
)
8312 saxdb_start_record(ctx
, bData
->mask
, 0);
8313 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8314 if(bData
->triggered
)
8315 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8317 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8319 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8321 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8322 saxdb_end_record(ctx
);
8324 saxdb_end_record(ctx
);
8328 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8330 saxdb_start_record(ctx
, name
, 0);
8331 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8332 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8334 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8336 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8338 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8340 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8341 saxdb_end_record(ctx
);
8345 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8347 saxdb_start_record(ctx
, name
, 0);
8348 if(giveownership
->staff_issuer
)
8349 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8350 if(giveownership
->old_owner
)
8351 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8352 if(giveownership
->target
)
8353 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8354 if(giveownership
->target_access
)
8355 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8356 if(giveownership
->reason
)
8357 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8358 if(giveownership
->issued
)
8359 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8360 if(giveownership
->previous
)
8361 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8362 saxdb_end_record(ctx
);
8366 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8370 enum levelOption lvlOpt
;
8371 enum charOption chOpt
;
8373 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8375 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8376 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8378 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8379 if(channel
->registrar
)
8380 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8381 if(channel
->greeting
)
8382 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8383 if(channel
->user_greeting
)
8384 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8385 if(channel
->topic_mask
)
8386 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8387 if(channel
->suspended
)
8388 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8389 if(channel
->giveownership
)
8390 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8392 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8393 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8394 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8395 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8396 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8398 buf
[0] = channel
->chOpts
[chOpt
];
8400 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8402 saxdb_end_record(ctx
);
8404 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8406 mod_chanmode_format(&channel
->modes
, buf
);
8407 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8410 high_present
= chanserv_write_users(ctx
, channel
->users
);
8411 chanserv_write_bans(ctx
, channel
->bans
);
8413 if(dict_size(channel
->notes
))
8417 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8418 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8420 struct note
*note
= iter_data(it
);
8421 saxdb_start_record(ctx
, iter_key(it
), 0);
8422 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8423 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8424 saxdb_end_record(ctx
);
8426 saxdb_end_record(ctx
);
8429 if(channel
->ownerTransfer
)
8430 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8431 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8432 saxdb_end_record(ctx
);
8436 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8440 saxdb_start_record(ctx
, ntype
->name
, 0);
8441 switch(ntype
->set_access_type
)
8443 case NOTE_SET_CHANNEL_ACCESS
:
8444 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8446 case NOTE_SET_CHANNEL_SETTER
:
8447 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8449 case NOTE_SET_PRIVILEGED
: default:
8450 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8453 switch(ntype
->visible_type
)
8455 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8456 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8457 case NOTE_VIS_PRIVILEGED
:
8458 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8460 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8461 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8462 saxdb_end_record(ctx
);
8466 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8468 struct do_not_register
*dnr
;
8471 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8473 dnr
= iter_data(it
);
8474 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8476 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8477 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8478 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8479 saxdb_end_record(ctx
);
8484 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8487 struct chanData
*channel
;
8489 /* Version Control*/
8490 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8491 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8492 saxdb_end_record(ctx
);
8495 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8496 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8497 chanserv_write_note_type(ctx
, iter_data(it
));
8498 saxdb_end_record(ctx
);
8501 saxdb_start_record(ctx
, KEY_DNR
, 1);
8502 write_dnrs_helper(ctx
, handle_dnrs
);
8503 write_dnrs_helper(ctx
, plain_dnrs
);
8504 write_dnrs_helper(ctx
, mask_dnrs
);
8505 saxdb_end_record(ctx
);
8508 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8509 for(channel
= channelList
; channel
; channel
= channel
->next
)
8510 chanserv_write_channel(ctx
, channel
);
8511 saxdb_end_record(ctx
);
8517 chanserv_db_cleanup(void) {
8519 unreg_part_func(handle_part
);
8521 unregister_channel(channelList
, "terminating.");
8522 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8523 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8524 free(chanserv_conf
.support_channels
.list
);
8525 dict_delete(handle_dnrs
);
8526 dict_delete(plain_dnrs
);
8527 dict_delete(mask_dnrs
);
8528 dict_delete(note_types
);
8529 free_string_list(chanserv_conf
.eightball
);
8530 free_string_list(chanserv_conf
.old_ban_names
);
8531 free_string_list(chanserv_conf
.set_shows
);
8532 free(set_shows_list
.list
);
8533 free(uset_shows_list
.list
);
8536 struct userData
*helper
= helperList
;
8537 helperList
= helperList
->next
;
8542 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8543 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8544 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8547 init_chanserv(const char *nick
)
8549 struct chanNode
*chan
;
8551 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8552 conf_register_reload(chanserv_conf_read
);
8554 reg_server_link_func(handle_server_link
);
8556 reg_new_channel_func(handle_new_channel
);
8557 reg_join_func(handle_join
);
8558 reg_part_func(handle_part
);
8559 reg_kick_func(handle_kick
);
8560 reg_topic_func(handle_topic
);
8561 reg_mode_change_func(handle_mode
);
8562 reg_nick_change_func(handle_nick_change
);
8564 reg_auth_func(handle_auth
);
8565 reg_handle_rename_func(handle_rename
);
8566 reg_unreg_func(handle_unreg
);
8568 handle_dnrs
= dict_new();
8569 dict_set_free_data(handle_dnrs
, free
);
8570 plain_dnrs
= dict_new();
8571 dict_set_free_data(plain_dnrs
, free
);
8572 mask_dnrs
= dict_new();
8573 dict_set_free_data(mask_dnrs
, free
);
8575 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8576 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8577 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8578 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8579 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8580 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8581 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8582 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8583 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8584 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8586 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8588 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8589 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8591 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8592 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8593 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8594 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8595 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8597 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8598 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8599 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8600 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8601 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8602 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8604 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8605 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8606 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8607 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8609 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8610 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8611 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8612 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8613 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8614 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8615 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8616 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8617 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8618 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8620 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8621 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8622 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8623 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8624 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8625 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8626 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8627 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8628 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8629 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8630 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8631 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8632 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8633 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8635 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8636 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8637 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8638 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8640 /* if you change dellamer access, see also places
8641 * like unbanme which have manager hardcoded. */
8642 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8643 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8645 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8647 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8649 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8650 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8651 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8652 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8653 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8654 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8655 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8656 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8657 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8658 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8659 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8660 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8662 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8663 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8665 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8666 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8667 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8668 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8670 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8671 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8672 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8673 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8674 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8676 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8677 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8678 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8679 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8680 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8681 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8682 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8684 /* Channel options */
8685 DEFINE_CHANNEL_OPTION(defaulttopic
);
8686 DEFINE_CHANNEL_OPTION(topicmask
);
8687 DEFINE_CHANNEL_OPTION(greeting
);
8688 DEFINE_CHANNEL_OPTION(usergreeting
);
8689 DEFINE_CHANNEL_OPTION(modes
);
8690 DEFINE_CHANNEL_OPTION(enfops
);
8691 DEFINE_CHANNEL_OPTION(enfhalfops
);
8692 DEFINE_CHANNEL_OPTION(automode
);
8693 DEFINE_CHANNEL_OPTION(protect
);
8694 DEFINE_CHANNEL_OPTION(enfmodes
);
8695 DEFINE_CHANNEL_OPTION(enftopic
);
8696 DEFINE_CHANNEL_OPTION(pubcmd
);
8697 DEFINE_CHANNEL_OPTION(userinfo
);
8698 DEFINE_CHANNEL_OPTION(dynlimit
);
8699 DEFINE_CHANNEL_OPTION(topicsnarf
);
8700 DEFINE_CHANNEL_OPTION(nodelete
);
8701 DEFINE_CHANNEL_OPTION(toys
);
8702 DEFINE_CHANNEL_OPTION(setters
);
8703 DEFINE_CHANNEL_OPTION(topicrefresh
);
8704 DEFINE_CHANNEL_OPTION(resync
);
8705 DEFINE_CHANNEL_OPTION(bantype
);
8706 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8707 DEFINE_CHANNEL_OPTION(bantimeout
);
8708 DEFINE_CHANNEL_OPTION(inviteme
);
8710 DEFINE_CHANNEL_OPTION(offchannel
);
8711 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8713 /* Alias set topic to set defaulttopic for compatibility. */
8714 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8717 DEFINE_USER_OPTION(autoinvite
);
8718 DEFINE_USER_OPTION(info
);
8719 DEFINE_USER_OPTION(autoop
);
8721 /* Alias uset autovoice to uset autoop. */
8722 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8724 note_types
= dict_new();
8725 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8728 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8729 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8730 service_register(chanserv
)->trigger
= '!';
8731 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8734 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8736 if(chanserv_conf
.channel_expire_frequency
)
8737 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8739 if(chanserv_conf
.ban_timeout_frequency
)
8740 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8742 if(chanserv_conf
.refresh_period
)
8744 time_t next_refresh
;
8745 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8746 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8747 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8750 if (autojoin_channels
&& chanserv
) {
8751 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8752 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8753 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8757 reg_exit_func(chanserv_db_cleanup
);
8758 message_register_table(msgtab
);