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_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
222 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
223 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
224 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
227 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
228 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
229 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
230 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
231 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
232 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
233 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
234 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
235 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
236 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
237 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
238 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
239 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
240 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
241 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
242 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
243 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
245 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
247 /* Channel management */
248 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
249 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
250 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
252 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
253 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
254 { "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" },
255 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
256 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
257 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
258 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
260 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
261 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
262 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
263 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
264 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
265 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
266 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
267 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
268 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
269 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
270 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
271 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
272 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
273 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
274 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
275 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
276 { "CSMSG_SET_MODES", "$bModes $b %s" },
277 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
278 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
279 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
280 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
283 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
284 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
285 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
286 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
287 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
288 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
289 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
290 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
291 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
292 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
293 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
294 { "CSMSG_SET_RESYNC", "$bResync $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_AUTOJOIN", "$bAutoJoin $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
305 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
306 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
307 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
308 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
309 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
312 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
313 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
315 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
316 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
317 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
318 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
319 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
320 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
321 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
323 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
324 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
325 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
326 { "CSMSG_PROTECT_NONE", "No users will be protected." },
327 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
328 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
329 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
331 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
332 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
333 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
334 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
335 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
337 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
338 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
339 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
340 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
341 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
343 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
344 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
345 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
346 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
347 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
349 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
350 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
351 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
352 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
353 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
354 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
356 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
357 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
358 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
359 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
360 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
361 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
362 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
363 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
364 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
366 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
367 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
368 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
370 /* Channel userlist */
371 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
372 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
373 /* uncomment if needed to adujust styles (and change code below)
374 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
375 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
376 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
377 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
378 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
379 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
381 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
382 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
383 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
385 /* Channel note list */
386 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
387 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
388 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
389 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
390 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
391 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
392 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
393 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
394 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
395 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
396 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
397 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
398 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
399 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
400 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
402 /* Channel [un]suspension */
403 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
404 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
405 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
406 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
407 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
408 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
409 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
411 /* Access information */
412 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
413 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
414 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
415 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
416 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
417 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
418 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
419 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
420 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
421 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
422 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
424 /* Seen information */
425 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
426 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
427 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
428 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
430 /* Names information */
431 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
432 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
434 /* Channel information */
435 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
436 { "CSMSG_BAR", "----------------------------------------"},
437 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
438 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
439 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
440 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
441 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
442 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
443 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
444 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
445 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
446 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
447 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
452 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
453 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
454 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
455 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
456 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
457 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
458 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
459 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
460 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
461 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
462 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
464 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
465 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
466 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
467 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
468 { "CSMSG_PEEK_OPS", "$bOps:$b" },
469 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
470 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
472 /* Network information */
473 { "CSMSG_NETWORK_INFO", "Network Information:" },
474 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
475 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
476 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
477 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
478 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
479 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
480 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
481 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
484 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
485 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
486 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
488 /* Channel searches */
489 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
490 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
491 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
492 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
494 /* Channel configuration */
495 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
496 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
497 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
498 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
501 { "CSMSG_USER_OPTIONS", "User Options:" },
502 // { "CSMSG_USER_PROTECTED", "That user is protected." },
505 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
506 { "CSMSG_PING_RESPONSE", "Pong!" },
507 { "CSMSG_WUT_RESPONSE", "wut" },
508 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
509 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
510 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
511 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
512 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
513 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
514 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
517 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
518 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
519 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
520 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
524 /* eject_user and unban_user flags */
525 #define ACTION_KICK 0x0001
526 #define ACTION_BAN 0x0002
527 #define ACTION_ADD_LAMER 0x0004
528 #define ACTION_ADD_TIMED_LAMER 0x0008
529 #define ACTION_UNBAN 0x0010
530 #define ACTION_DEL_LAMER 0x0020
532 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
533 #define MODELEN 40 + KEYLEN
537 #define CSFUNC_ARGS user, channel, argc, argv, cmd
539 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
540 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
541 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
542 reply("MSG_MISSING_PARAMS", argv[0]); \
546 DECLARE_LIST(dnrList
, struct do_not_register
*);
547 DEFINE_LIST(dnrList
, struct do_not_register
*);
549 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
551 struct userNode
*chanserv
;
554 extern struct string_list
*autojoin_channels
;
555 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
556 static struct log_type
*CS_LOG
;
557 struct adduserPending
* adduser_pendings
= NULL
;
558 unsigned int adduser_pendings_count
= 0;
559 unsigned long god_timeout
;
563 struct channelList support_channels
;
564 struct mod_chanmode default_modes
;
566 unsigned long db_backup_frequency
;
567 unsigned long channel_expire_frequency
;
568 unsigned long ban_timeout_frequency
;
571 unsigned int adjust_delay
;
572 long channel_expire_delay
;
573 unsigned int nodelete_level
;
575 unsigned int adjust_threshold
;
576 int join_flood_threshold
;
578 unsigned int greeting_length
;
579 unsigned int refresh_period
;
580 unsigned int giveownership_period
;
582 unsigned int max_owned
;
583 unsigned int max_chan_users
;
584 unsigned int max_chan_bans
; /* lamers */
585 unsigned int max_userinfo_length
;
587 struct string_list
*set_shows
;
588 struct string_list
*eightball
;
589 struct string_list
*old_ban_names
;
591 const char *ctcp_short_ban_duration
;
592 const char *ctcp_long_ban_duration
;
594 const char *irc_operator_epithet
;
595 const char *network_helper_epithet
;
596 const char *support_helper_epithet
;
601 struct userNode
*user
;
602 struct userNode
*bot
;
603 struct chanNode
*channel
;
605 unsigned short lowest
;
606 unsigned short highest
;
607 struct userData
**users
;
608 struct helpfile_table table
;
611 enum note_access_type
613 NOTE_SET_CHANNEL_ACCESS
,
614 NOTE_SET_CHANNEL_SETTER
,
618 enum note_visible_type
621 NOTE_VIS_CHANNEL_USERS
,
627 enum note_access_type set_access_type
;
629 unsigned int min_opserv
;
630 unsigned short min_ulevel
;
632 enum note_visible_type visible_type
;
633 unsigned int max_length
;
640 struct note_type
*type
;
641 char setter
[NICKSERV_HANDLE_LEN
+1];
645 static unsigned int registered_channels
;
646 static unsigned int banCount
;
648 static const struct {
651 unsigned short level
;
653 } accessLevels
[] = { /* MUST be orderd less to most! */
654 { "peon", "Peon", UL_PEON
, '+' },
655 { "halfop", "HalfOp", UL_HALFOP
, '%' },
656 { "op", "Op", UL_OP
, '@' },
657 { "manager", "Manager", UL_MANAGER
, '%' },
658 { "coowner", "Coowner", UL_COOWNER
, '*' },
659 { "owner", "Owner", UL_OWNER
, '!' },
660 { "helper", "BUG:", UL_HELPER
, 'X' }
663 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
664 static const struct {
667 unsigned short default_value
;
668 unsigned int old_idx
;
669 unsigned int old_flag
;
670 unsigned short flag_value
;
672 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
673 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
674 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
675 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
676 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
677 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
678 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
679 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
680 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
683 struct charOptionValues
{
686 } automodeValues
[] = {
687 { 'n', "CSMSG_AUTOMODE_NONE" },
688 { 'y', "CSMSG_AUTOMODE_NORMAL" },
689 { 'v', "CSMSG_AUTOMODE_VOICE" },
690 { 'h', "CSMSG_AUTOMODE_HOP" },
691 { 'o', "CSMSG_AUTOMODE_OP" },
692 { 'm', "CSMSG_AUTOMODE_MUTE" },
693 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
694 }, protectValues
[] = {
695 { 'a', "CSMSG_PROTECT_ALL" },
696 { 'e', "CSMSG_PROTECT_EQUAL" },
697 { 'l', "CSMSG_PROTECT_LOWER" },
698 { 'n', "CSMSG_PROTECT_NONE" }
700 { 'd', "CSMSG_TOYS_DISABLED" },
701 { 'n', "CSMSG_TOYS_PRIVATE" },
702 { 'p', "CSMSG_TOYS_PUBLIC" }
703 }, topicRefreshValues
[] = {
704 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
705 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
706 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
707 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
708 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
709 }, ctcpReactionValues
[] = {
710 { 'n', "CSMSG_CTCPREACTION_NONE" },
711 { 'k', "CSMSG_CTCPREACTION_KICK" },
712 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
713 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
714 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
715 }, banTimeoutValues
[] = {
716 { '0', "CSMSG_BANTIMEOUT_NONE" },
717 { '1', "CSMSG_BANTIMEOUT_10M" },
718 { '2', "CSMSG_BANTIMEOUT_2H" },
719 { '3', "CSMSG_BANTIMEOUT_4H" },
720 { '4', "CSMSG_BANTIMEOUT_1D" },
721 { '5', "CSMSG_BANTIMEOUT_1W" }
724 { 'n', "CSMSG_RESYNC_NEVER" },
725 { '1', "CSMSG_RESYNC_3_HOURS" },
726 { '2', "CSMSG_RESYNC_6_HOURS" },
727 { '3', "CSMSG_RESYNC_12_HOURS" },
728 { '4', "CSMSG_RESYNC_24_HOURS" }
731 static const struct {
735 unsigned int old_idx
;
737 struct charOptionValues
*values
;
739 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
740 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
741 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
742 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
743 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
744 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
745 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
748 struct userData
*helperList
;
749 struct chanData
*channelList
;
750 static struct module *chanserv_module
;
751 static unsigned int userCount
;
752 unsigned int chanserv_read_version
= 0; /* db version control */
754 #define CHANSERV_DB_VERSION 2
756 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
757 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
760 user_level_from_name(const char *name
, unsigned short clamp_level
)
762 unsigned int level
= 0, ii
;
764 level
= strtoul(name
, NULL
, 10);
765 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
766 if(!irccasecmp(name
, accessLevels
[ii
].name
))
767 level
= accessLevels
[ii
].level
;
768 if(level
> clamp_level
)
774 user_level_name_from_level(int level
)
782 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
783 if(level
>= accessLevels
[ii
].level
)
784 highest
= accessLevels
[ii
].title
;
790 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
793 *minl
= strtoul(arg
, &sep
, 10);
801 *maxl
= strtoul(sep
+1, &sep
, 10);
809 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
811 struct userData
*uData
, **head
;
813 if(!channel
|| !handle
)
816 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
817 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
819 for(uData
= helperList
;
820 uData
&& uData
->handle
!= handle
;
821 uData
= uData
->next
);
825 uData
= calloc(1, sizeof(struct userData
));
826 uData
->handle
= handle
;
828 uData
->access
= UL_HELPER
;
834 uData
->next
= helperList
;
836 helperList
->prev
= uData
;
844 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
845 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
848 head
= &(channel
->users
);
851 if(uData
&& (uData
!= *head
))
853 /* Shuffle the user to the head of whatever list he was in. */
855 uData
->next
->prev
= uData
->prev
;
857 uData
->prev
->next
= uData
->next
;
863 (**head
).prev
= uData
;
870 /* Returns non-zero if user has at least the minimum access.
871 * exempt_owner is set when handling !set, so the owner can set things
874 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
876 struct userData
*uData
;
877 struct chanData
*cData
= channel
->channel_info
;
878 unsigned short minimum
= cData
->lvlOpts
[opt
];
881 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
884 if(minimum
<= uData
->access
)
886 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
891 /* Scan for other users authenticated to the same handle
892 still in the channel. If so, keep them listed as present.
894 user is optional, if not null, it skips checking that userNode
895 (for the handle_part function) */
897 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
901 if(IsSuspended(uData
->channel
)
902 || IsUserSuspended(uData
)
903 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
915 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
917 unsigned int eflags
, argc
;
919 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
921 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
922 if(!channel
->channel_info
923 || IsSuspended(channel
->channel_info
)
925 || !ircncasecmp(text
, "ACTION ", 7))
927 /* We dont punish people we know -Rubin
928 * * Figure out the minimum level needed to CTCP the channel *
930 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
933 /* If they are a user of the channel, they are exempt */
934 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
936 /* We need to enforce against them; do so. */
939 argv
[1] = user
->nick
;
941 if(GetUserMode(channel
, user
))
942 eflags
|= ACTION_KICK
;
943 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
944 default: case 'n': return;
946 eflags
|= ACTION_KICK
;
949 eflags
|= ACTION_BAN
;
952 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
953 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
956 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
957 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
960 argv
[argc
++] = bad_ctcp_reason
;
961 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
965 chanserv_create_note_type(const char *name
)
967 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
968 strcpy(ntype
->name
, name
);
970 dict_insert(note_types
, ntype
->name
, ntype
);
975 chanserv_deref_note_type(void *data
)
977 struct note_type
*ntype
= data
;
979 if(--ntype
->refs
> 0)
985 chanserv_flush_note_type(struct note_type
*ntype
)
987 struct chanData
*cData
;
988 for(cData
= channelList
; cData
; cData
= cData
->next
)
989 dict_remove(cData
->notes
, ntype
->name
);
993 chanserv_truncate_notes(struct note_type
*ntype
)
995 struct chanData
*cData
;
997 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
999 for(cData
= channelList
; cData
; cData
= cData
->next
) {
1000 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1003 if(strlen(note
->note
) <= ntype
->max_length
)
1005 dict_remove2(cData
->notes
, ntype
->name
, 1);
1006 note
= realloc(note
, size
);
1007 note
->note
[ntype
->max_length
] = 0;
1008 dict_insert(cData
->notes
, ntype
->name
, note
);
1012 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1014 static struct note
*
1015 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1018 unsigned int len
= strlen(text
);
1020 if(len
> type
->max_length
) len
= type
->max_length
;
1021 note
= calloc(1, sizeof(*note
) + len
);
1023 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1024 memcpy(note
->note
, text
, len
);
1025 note
->note
[len
] = 0;
1026 dict_insert(channel
->notes
, type
->name
, note
);
1032 chanserv_free_note(void *data
)
1034 struct note
*note
= data
;
1036 chanserv_deref_note_type(note
->type
);
1037 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1041 static MODCMD_FUNC(cmd_createnote
) {
1042 struct note_type
*ntype
;
1043 unsigned int arg
= 1, existed
= 0, max_length
;
1045 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1048 ntype
= chanserv_create_note_type(argv
[arg
]);
1049 if(!irccasecmp(argv
[++arg
], "privileged"))
1052 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1053 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1055 else if(!irccasecmp(argv
[arg
], "channel"))
1057 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1060 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1063 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1064 ntype
->set_access
.min_ulevel
= ulvl
;
1066 else if(!irccasecmp(argv
[arg
], "setter"))
1068 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1072 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1076 if(!irccasecmp(argv
[++arg
], "privileged"))
1077 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1078 else if(!irccasecmp(argv
[arg
], "channel_users"))
1079 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1080 else if(!irccasecmp(argv
[arg
], "all"))
1081 ntype
->visible_type
= NOTE_VIS_ALL
;
1083 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1087 if((arg
+1) >= argc
) {
1088 reply("MSG_MISSING_PARAMS", argv
[0]);
1091 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1092 if(max_length
< 20 || max_length
> 450)
1094 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1097 if(existed
&& (max_length
< ntype
->max_length
))
1099 ntype
->max_length
= max_length
;
1100 chanserv_truncate_notes(ntype
);
1102 ntype
->max_length
= max_length
;
1105 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1107 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1112 dict_remove(note_types
, ntype
->name
);
1116 static MODCMD_FUNC(cmd_removenote
) {
1117 struct note_type
*ntype
;
1120 ntype
= dict_find(note_types
, argv
[1], NULL
);
1121 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1124 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1131 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1134 chanserv_flush_note_type(ntype
);
1136 dict_remove(note_types
, argv
[1]);
1137 reply("CSMSG_NOTE_DELETED", argv
[1]);
1142 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1146 if(orig
->modes_set
& change
->modes_clear
)
1148 if(orig
->modes_clear
& change
->modes_set
)
1150 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1151 && strcmp(orig
->new_key
, change
->new_key
))
1153 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1154 && (orig
->new_limit
!= change
->new_limit
))
1159 static char max_length_text
[MAXLEN
+1][16];
1161 static struct helpfile_expansion
1162 chanserv_expand_variable(const char *variable
)
1164 struct helpfile_expansion exp
;
1166 if(!irccasecmp(variable
, "notes"))
1169 exp
.type
= HF_TABLE
;
1170 exp
.value
.table
.length
= 1;
1171 exp
.value
.table
.width
= 3;
1172 exp
.value
.table
.flags
= 0;
1173 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1174 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1175 exp
.value
.table
.contents
[0][0] = "Note Type";
1176 exp
.value
.table
.contents
[0][1] = "Visibility";
1177 exp
.value
.table
.contents
[0][2] = "Max Length";
1178 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1180 struct note_type
*ntype
= iter_data(it
);
1183 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1184 row
= exp
.value
.table
.length
++;
1185 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1186 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1187 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1188 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1190 if(!max_length_text
[ntype
->max_length
][0])
1191 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1192 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1197 exp
.type
= HF_STRING
;
1198 exp
.value
.str
= NULL
;
1202 static struct chanData
*
1203 register_channel(struct chanNode
*cNode
, char *registrar
)
1205 struct chanData
*channel
;
1206 enum levelOption lvlOpt
;
1207 enum charOption chOpt
;
1209 channel
= calloc(1, sizeof(struct chanData
));
1211 channel
->notes
= dict_new();
1212 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1214 channel
->registrar
= strdup(registrar
);
1215 channel
->registered
= now
;
1216 channel
->visited
= now
;
1217 channel
->limitAdjusted
= now
;
1218 channel
->ownerTransfer
= now
;
1219 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1220 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1221 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1222 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1223 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1225 channel
->prev
= NULL
;
1226 channel
->next
= channelList
;
1229 channelList
->prev
= channel
;
1230 channelList
= channel
;
1231 registered_channels
++;
1233 channel
->channel
= cNode
;
1235 cNode
->channel_info
= channel
;
1240 static struct userData
*
1241 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1243 struct userData
*ud
;
1245 if(access
> UL_OWNER
)
1248 ud
= calloc(1, sizeof(*ud
));
1249 ud
->channel
= channel
;
1250 ud
->handle
= handle
;
1252 ud
->access
= access
;
1253 ud
->info
= info
? strdup(info
) : NULL
;
1256 ud
->next
= channel
->users
;
1258 channel
->users
->prev
= ud
;
1259 channel
->users
= ud
;
1261 channel
->userCount
++;
1265 ud
->u_next
= ud
->handle
->channels
;
1267 ud
->u_next
->u_prev
= ud
;
1268 ud
->handle
->channels
= ud
;
1270 ud
->flags
= USER_FLAGS_DEFAULT
;
1274 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1277 del_channel_user(struct userData
*user
, int do_gc
)
1279 struct chanData
*channel
= user
->channel
;
1281 channel
->userCount
--;
1285 user
->prev
->next
= user
->next
;
1287 channel
->users
= user
->next
;
1289 user
->next
->prev
= user
->prev
;
1292 user
->u_prev
->u_next
= user
->u_next
;
1294 user
->handle
->channels
= user
->u_next
;
1296 user
->u_next
->u_prev
= user
->u_prev
;
1300 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1301 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1302 unregister_channel(channel
, "lost all users.");
1306 static struct adduserPending
*
1307 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1309 struct adduserPending
*ap
;
1310 ap
= calloc(1,sizeof(struct adduserPending
));
1311 ap
->channel
= channel
;
1314 ap
->created
= time(NULL
);
1316 /* ap->prev defaults to NULL already.. */
1317 ap
->next
= adduser_pendings
;
1318 if(adduser_pendings
)
1319 adduser_pendings
->prev
= ap
;
1320 adduser_pendings
= ap
;
1321 adduser_pendings_count
++;
1326 del_adduser_pending(struct adduserPending
*ap
)
1329 ap
->prev
->next
= ap
->next
;
1331 adduser_pendings
= ap
->next
;
1334 ap
->next
->prev
= ap
->prev
;
1338 static void expire_adduser_pending();
1340 /* find_adduser_pending(channel, user) will find an arbitrary record
1341 * from user, channel, or user and channel.
1342 * if user or channel are NULL, they will match any records.
1344 static struct adduserPending
*
1345 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1347 struct adduserPending
*ap
;
1349 expire_adduser_pending(); /* why not here.. */
1351 if(!channel
&& !user
) /* 2 nulls matches all */
1352 return(adduser_pendings
);
1353 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1355 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1362 /* Remove all pendings for a user or channel
1364 * called in nickserv.c DelUser() and proto-* unregister_channel()
1367 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1369 struct adduserPending
*ap
;
1371 /* So this is a bit wastefull, i hate dealing with linked lists.
1372 * if its a problem we'll rewrite it right */
1373 while((ap
= find_adduser_pending(channel
, user
))) {
1374 del_adduser_pending(ap
);
1378 /* Called from nickserv.c cmd_auth after someone auths */
1380 process_adduser_pending(struct userNode
*user
)
1382 struct adduserPending
*ap
;
1383 if(!user
->handle_info
)
1384 return; /* not associated with an account */
1385 while((ap
= find_adduser_pending(NULL
, user
)))
1387 struct userData
*actee
;
1388 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1390 /* Already on the userlist. do nothing*/
1394 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1395 scan_user_presence(actee
, NULL
);
1397 del_adduser_pending(ap
);
1402 expire_adduser_pending()
1404 struct adduserPending
*ap
, *ap_next
;
1405 ap
= adduser_pendings
;
1408 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1410 ap_next
= ap
->next
; /* save next */
1411 del_adduser_pending(ap
); /* free and relink */
1412 ap
= ap_next
; /* advance */
1419 static void expire_ban(void *data
);
1422 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1425 unsigned int ii
, l1
, l2
;
1430 bd
= malloc(sizeof(struct banData
));
1432 bd
->channel
= channel
;
1434 bd
->triggered
= triggered
;
1435 bd
->expires
= expires
;
1437 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1439 extern const char *hidden_host_suffix
;
1440 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1444 l2
= strlen(old_name
);
1447 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1449 new_mask
= alloca(MAXLEN
);
1450 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1453 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1455 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1456 bd
->reason
= strdup(reason
);
1459 timeq_add(expires
, expire_ban
, bd
);
1462 bd
->next
= channel
->bans
; /* lamers */
1464 channel
->bans
->prev
= bd
;
1466 channel
->banCount
++;
1473 del_channel_ban(struct banData
*ban
)
1475 ban
->channel
->banCount
--;
1479 ban
->prev
->next
= ban
->next
;
1481 ban
->channel
->bans
= ban
->next
;
1484 ban
->next
->prev
= ban
->prev
;
1487 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1496 expire_ban(void *data
) /* lamer.. */
1498 struct banData
*bd
= data
;
1499 if(!IsSuspended(bd
->channel
))
1501 struct banList bans
;
1502 struct mod_chanmode change
;
1504 bans
= bd
->channel
->channel
->banlist
;
1505 mod_chanmode_init(&change
);
1506 for(ii
=0; ii
<bans
.used
; ii
++)
1508 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1511 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1512 change
.args
[0].u
.hostmask
= bd
->mask
;
1513 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1519 del_channel_ban(bd
);
1522 static void chanserv_expire_suspension(void *data
);
1525 unregister_channel(struct chanData
*channel
, const char *reason
)
1527 struct mod_chanmode change
;
1528 char msgbuf
[MAXLEN
];
1530 /* After channel unregistration, the following must be cleaned
1532 - Channel information.
1534 - Channel bans. (lamers)
1535 - Channel suspension data.
1536 - adduser_pending data.
1537 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1543 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1547 mod_chanmode_init(&change
);
1548 change
.modes_clear
|= MODE_REGISTERED
;
1549 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1552 wipe_adduser_pending(channel
->channel
, NULL
);
1554 while(channel
->users
)
1555 del_channel_user(channel
->users
, 0);
1557 while(channel
->bans
)
1558 del_channel_ban(channel
->bans
);
1560 free(channel
->topic
);
1561 free(channel
->registrar
);
1562 free(channel
->greeting
);
1563 free(channel
->user_greeting
);
1564 free(channel
->topic_mask
);
1567 channel
->prev
->next
= channel
->next
;
1569 channelList
= channel
->next
;
1572 channel
->next
->prev
= channel
->prev
;
1574 if(channel
->suspended
)
1576 struct chanNode
*cNode
= channel
->channel
;
1577 struct suspended
*suspended
, *next_suspended
;
1579 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1581 next_suspended
= suspended
->previous
;
1582 free(suspended
->suspender
);
1583 free(suspended
->reason
);
1584 if(suspended
->expires
)
1585 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1590 cNode
->channel_info
= NULL
;
1592 channel
->channel
->channel_info
= NULL
;
1594 dict_delete(channel
->notes
);
1595 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1596 if(!IsSuspended(channel
))
1597 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1598 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1599 UnlockChannel(channel
->channel
);
1601 registered_channels
--;
1605 expire_channels(UNUSED_ARG(void *data
))
1607 struct chanData
*channel
, *next
;
1608 struct userData
*user
;
1609 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1611 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1612 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1614 for(channel
= channelList
; channel
; channel
= next
)
1616 next
= channel
->next
;
1618 /* See if the channel can be expired. */
1619 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1620 || IsProtected(channel
))
1623 /* Make sure there are no high-ranking users still in the channel. */
1624 for(user
=channel
->users
; user
; user
=user
->next
)
1625 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1630 /* Unregister the channel */
1631 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1632 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1633 unregister_channel(channel
, "registration expired.");
1636 if(chanserv_conf
.channel_expire_frequency
)
1637 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1641 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
, int protect_invitables
)
1643 char protect
= channel
->chOpts
[chProtect
];
1644 struct userData
*cs_victim
, *cs_aggressor
;
1646 /* If victim access level is greater than set invitelevel, don't let
1647 * us kick them, but don't consider it punishment if someone else does
1651 if(victim
== aggressor
)
1653 /* Don't protect if the victim isn't authenticated (because they
1654 can't be a channel user), unless we are to protect non-users
1657 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1659 /* If they have enough access to invite themselvs through a ban,
1660 * and its us kicking them, don't. -Rubin */
1661 if(protect_invitables
==true && cs_victim
&& (cs_victim
->access
>= channel
->lvlOpts
[lvlInviteMe
]))
1667 if(protect
!= 'a' && !cs_victim
)
1670 /* Protect if the aggressor isn't a user because at this point,
1671 the aggressor can only be less than or equal to the victim. */
1673 /* Not protected from chanserv except above */
1674 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1675 if(aggressor
== chanserv
)
1678 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1682 /* If the aggressor was a user, then the victim can't be helped. */
1689 if(cs_victim
->access
> cs_aggressor
->access
)
1694 if(cs_victim
->access
>= cs_aggressor
->access
)
1703 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1705 struct chanData
*cData
= channel
->channel_info
;
1706 struct userData
*cs_victim
;
1708 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1709 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1710 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1713 reply("CSMSG_OPBY_LOCKED");
1715 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1723 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1725 struct chanData
*cData
= channel
->channel_info
;
1726 struct userData
*cs_victim
;
1728 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1729 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1730 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1732 reply("CSMSG_HOPBY_LOCKED");
1741 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1743 if(IsService(victim
))
1745 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1749 if(protect_user(victim
, user
, channel
->channel_info
, false))
1751 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1759 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1761 if(IsService(victim
))
1763 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1767 if(protect_user(victim
, user
, channel
->channel_info
, false))
1769 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1776 static struct do_not_register
*
1777 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1779 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1780 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1781 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1782 strcpy(dnr
->reason
, reason
);
1784 if(dnr
->chan_name
[0] == '*')
1785 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1786 else if(strpbrk(dnr
->chan_name
, "*?"))
1787 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1789 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1793 static struct dnrList
1794 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1796 struct dnrList list
;
1798 struct do_not_register
*dnr
;
1800 dnrList_init(&list
);
1801 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1802 dnrList_append(&list
, dnr
);
1803 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1804 dnrList_append(&list
, dnr
);
1806 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1807 if(match_ircglob(chan_name
, iter_key(it
)))
1808 dnrList_append(&list
, iter_data(it
));
1813 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1815 struct dnrList list
;
1816 struct do_not_register
*dnr
;
1818 char buf
[INTERVALLEN
];
1820 list
= chanserv_find_dnrs(chan_name
, handle
);
1821 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1823 dnr
= list
.list
[ii
];
1826 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1827 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1830 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1833 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1838 struct do_not_register
*
1839 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1841 struct do_not_register
*dnr
;
1844 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1848 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1850 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1851 if(match_ircglob(chan_name
, iter_key(it
)))
1852 return iter_data(it
);
1857 static CHANSERV_FUNC(cmd_noregister
)
1860 struct do_not_register
*dnr
;
1861 char buf
[INTERVALLEN
];
1862 unsigned int matches
;
1868 reply("CSMSG_DNR_SEARCH_RESULTS");
1869 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1872 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1874 dnr
= iter_data(it
);
1876 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1878 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1881 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1883 dnr
= iter_data(it
);
1885 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1887 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1890 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1892 dnr
= iter_data(it
);
1894 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1896 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1901 reply("MSG_MATCH_COUNT", matches
);
1903 reply("MSG_NO_MATCHES");
1909 if(!IsChannelName(target
) && (*target
!= '*'))
1911 reply("CSMSG_NOT_DNR", target
);
1917 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1918 if((*target
== '*') && !get_handle_info(target
+ 1))
1920 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1923 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1924 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1928 reply("CSMSG_DNR_SEARCH_RESULTS");
1929 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1932 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1934 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1936 reply("MSG_NO_MATCHES");
1940 static CHANSERV_FUNC(cmd_allowregister
)
1942 const char *chan_name
= argv
[1];
1944 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1946 dict_remove(handle_dnrs
, chan_name
+1);
1947 reply("CSMSG_DNR_REMOVED", chan_name
);
1949 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1951 dict_remove(plain_dnrs
, chan_name
);
1952 reply("CSMSG_DNR_REMOVED", chan_name
);
1954 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1956 dict_remove(mask_dnrs
, chan_name
);
1957 reply("CSMSG_DNR_REMOVED", chan_name
);
1961 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1968 chanserv_get_owned_count(struct handle_info
*hi
)
1970 struct userData
*cList
;
1973 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1974 if(cList
->access
== UL_OWNER
)
1979 static CHANSERV_FUNC(cmd_register
)
1981 struct handle_info
*handle
;
1982 struct chanData
*cData
;
1983 struct modeNode
*mn
;
1984 char reason
[MAXLEN
];
1986 unsigned int new_channel
, force
=0;
1987 struct do_not_register
*dnr
;
1990 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
1991 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
1997 if(channel
->channel_info
)
1999 reply("CSMSG_ALREADY_REGGED", channel
->name
);
2003 if(channel
->bad_channel
)
2005 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
2009 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
2011 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
2016 chan_name
= channel
->name
;
2022 reply("MSG_MISSING_PARAMS", cmd
->name
);
2023 svccmd_send_help_brief(user
, chanserv
, cmd
);
2026 if(!IsChannelName(argv
[1]))
2028 reply("MSG_NOT_CHANNEL_NAME");
2032 if(opserv_bad_channel(argv
[1]))
2034 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2039 chan_name
= argv
[1];
2042 if(argc
>= (new_channel
+2))
2044 if(!IsHelping(user
))
2046 reply("CSMSG_PROXY_FORBIDDEN");
2050 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2052 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2053 dnr
= chanserv_is_dnr(chan_name
, handle
);
2055 /* Check if they are over the limit.. */
2056 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2058 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2065 handle
= user
->handle_info
;
2066 dnr
= chanserv_is_dnr(chan_name
, handle
);
2067 /* Check if they are over the limit.. */
2068 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2070 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2073 /* Check if another service is in the channel */
2075 for(n
= 0; n
< channel
->members
.used
; n
++)
2077 mn
= channel
->members
.list
[n
];
2078 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2080 reply("CSMSG_ANOTHER_SERVICE");
2087 if(!IsHelping(user
))
2088 reply("CSMSG_DNR_CHANNEL", chan_name
);
2090 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2094 /* now handled above for message specilization *
2095 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2097 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2103 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2105 cData
= register_channel(channel
, user
->handle_info
->handle
);
2106 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2107 cData
->modes
= chanserv_conf
.default_modes
;
2109 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2110 if (IsOffChannel(cData
))
2112 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2116 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2117 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2118 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2120 mod_chanmode_announce(chanserv
, channel
, change
);
2121 mod_chanmode_free(change
);
2124 /* Initialize the channel's max user record. */
2125 cData
->max
= channel
->members
.used
;
2127 if(handle
!= user
->handle_info
)
2128 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2131 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2132 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2133 handle
->handle
, user
->handle_info
->handle
);
2138 make_confirmation_string(struct userData
*uData
)
2140 static char strbuf
[16];
2145 for(src
= uData
->handle
->handle
; *src
; )
2146 accum
= accum
* 31 + toupper(*src
++);
2148 for(src
= uData
->channel
->channel
->name
; *src
; )
2149 accum
= accum
* 31 + toupper(*src
++);
2150 sprintf(strbuf
, "%08x", accum
);
2154 static CHANSERV_FUNC(cmd_unregister
)
2157 char reason
[MAXLEN
];
2158 struct chanData
*cData
;
2159 struct userData
*uData
;
2161 cData
= channel
->channel_info
;
2164 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2168 uData
= GetChannelUser(cData
, user
->handle_info
);
2169 if(!uData
|| (uData
->access
< UL_OWNER
))
2171 reply("CSMSG_NO_ACCESS");
2175 if(IsProtected(cData
))
2177 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2181 if(!IsHelping(user
))
2183 const char *confirm_string
;
2184 if(IsSuspended(cData
))
2186 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2189 confirm_string
= make_confirmation_string(uData
);
2190 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2192 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2197 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2198 name
= strdup(channel
->name
);
2199 unregister_channel(cData
, reason
);
2200 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2201 reply("CSMSG_UNREG_SUCCESS", name
);
2207 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2209 extern struct userNode
*spamserv
;
2210 struct mod_chanmode
*change
;
2212 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2214 change
= mod_chanmode_alloc(2);
2216 change
->args
[0].mode
= MODE_CHANOP
;
2217 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2218 change
->args
[1].mode
= MODE_CHANOP
;
2219 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2223 change
= mod_chanmode_alloc(1);
2225 change
->args
[0].mode
= MODE_CHANOP
;
2226 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2229 mod_chanmode_announce(chanserv
, channel
, change
);
2230 mod_chanmode_free(change
);
2233 static CHANSERV_FUNC(cmd_move
)
2235 struct mod_chanmode change
;
2236 struct chanNode
*target
;
2237 struct modeNode
*mn
;
2238 struct userData
*uData
;
2239 struct do_not_register
*dnr
;
2240 int chanserv_join
= 0, spamserv_join
;
2244 if(IsProtected(channel
->channel_info
))
2246 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2250 if(!IsChannelName(argv
[1]))
2252 reply("MSG_NOT_CHANNEL_NAME");
2256 if(opserv_bad_channel(argv
[1]))
2258 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2262 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2264 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2266 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2268 if(!IsHelping(user
))
2269 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2271 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2277 mod_chanmode_init(&change
);
2278 if(!(target
= GetChannel(argv
[1])))
2280 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2281 if(!IsSuspended(channel
->channel_info
))
2284 else if(target
->channel_info
)
2286 reply("CSMSG_ALREADY_REGGED", target
->name
);
2289 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2290 && !IsHelping(user
))
2292 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2295 else if(!IsSuspended(channel
->channel_info
))
2300 /* Clear MODE_REGISTERED from old channel, add it to new. */
2302 change
.modes_clear
= MODE_REGISTERED
;
2303 mod_chanmode_announce(chanserv
, channel
, &change
);
2304 change
.modes_clear
= 0;
2305 change
.modes_set
= MODE_REGISTERED
;
2306 mod_chanmode_announce(chanserv
, target
, &change
);
2309 /* Move the channel_info to the target channel; it
2310 shouldn't be necessary to clear timeq callbacks
2311 for the old channel. */
2312 target
->channel_info
= channel
->channel_info
;
2313 target
->channel_info
->channel
= target
;
2314 channel
->channel_info
= NULL
;
2316 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2319 ss_cs_join_channel(target
, spamserv_join
);
2321 if(!IsSuspended(target
->channel_info
))
2323 char reason2
[MAXLEN
];
2324 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2325 DelChannelUser(chanserv
, channel
, reason2
, 0);
2328 UnlockChannel(channel
);
2329 LockChannel(target
);
2330 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2331 channel
->name
, target
->name
, user
->handle_info
->handle
);
2333 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2338 merge_users(struct chanData
*source
, struct chanData
*target
)
2340 struct userData
*suData
, *tuData
, *next
;
2346 /* Insert the source's users into the scratch area. */
2347 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2348 dict_insert(merge
, suData
->handle
->handle
, suData
);
2350 /* Iterate through the target's users, looking for
2351 users common to both channels. The lower access is
2352 removed from either the scratch area or target user
2354 for(tuData
= target
->users
; tuData
; tuData
= next
)
2356 struct userData
*choice
;
2358 next
= tuData
->next
;
2360 /* If a source user exists with the same handle as a target
2361 channel's user, resolve the conflict by removing one. */
2362 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2366 /* Pick the data we want to keep. */
2367 /* If the access is the same, use the later seen time. */
2368 if(suData
->access
== tuData
->access
)
2369 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2370 else /* Otherwise, keep the higher access level. */
2371 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2373 /* Remove the user that wasn't picked. */
2374 if(choice
== tuData
)
2376 dict_remove(merge
, suData
->handle
->handle
);
2377 del_channel_user(suData
, 0);
2380 del_channel_user(tuData
, 0);
2383 /* Move the remaining users to the target channel. */
2384 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2386 suData
= iter_data(it
);
2388 /* Insert the user into the target channel's linked list. */
2389 suData
->prev
= NULL
;
2390 suData
->next
= target
->users
;
2391 suData
->channel
= target
;
2394 target
->users
->prev
= suData
;
2395 target
->users
= suData
;
2397 /* Update the user counts for the target channel; the
2398 source counts are left alone. */
2399 target
->userCount
++;
2402 /* Possible to assert (source->users == NULL) here. */
2403 source
->users
= NULL
;
2408 merge_bans(struct chanData
*source
, struct chanData
*target
)
2410 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2412 /* Hold on to the original head of the target ban list
2413 to avoid comparing source bans with source bans. */
2414 tFront
= target
->bans
;
2416 /* Perform a totally expensive O(n*m) merge, ick. */
2417 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2419 /* Flag to track whether the ban's been moved
2420 to the destination yet. */
2423 /* Possible to assert (sbData->prev == NULL) here. */
2424 sNext
= sbData
->next
;
2426 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2428 tNext
= tbData
->next
;
2430 /* Perform two comparisons between each source
2431 and target ban, conflicts are resolved by
2432 keeping the broader ban and copying the later
2433 expiration and triggered time. */
2434 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2436 /* There is a broader ban in the target channel that
2437 overrides one in the source channel; remove the
2438 source ban and break. */
2439 if(sbData
->expires
> tbData
->expires
)
2440 tbData
->expires
= sbData
->expires
;
2441 if(sbData
->triggered
> tbData
->triggered
)
2442 tbData
->triggered
= sbData
->triggered
;
2443 del_channel_ban(sbData
);
2446 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2448 /* There is a broader ban in the source channel that
2449 overrides one in the target channel; remove the
2450 target ban, fall through and move the source over. */
2451 if(tbData
->expires
> sbData
->expires
)
2452 sbData
->expires
= tbData
->expires
;
2453 if(tbData
->triggered
> sbData
->triggered
)
2454 sbData
->triggered
= tbData
->triggered
;
2455 if(tbData
== tFront
)
2457 del_channel_ban(tbData
);
2460 /* Source bans can override multiple target bans, so
2461 we allow a source to run through this loop multiple
2462 times, but we can only move it once. */
2467 /* Remove the source ban from the source ban list. */
2469 sbData
->next
->prev
= sbData
->prev
;
2471 /* Modify the source ban's associated channel. */
2472 sbData
->channel
= target
;
2474 /* Insert the ban into the target channel's linked list. */
2475 sbData
->prev
= NULL
;
2476 sbData
->next
= target
->bans
;
2479 target
->bans
->prev
= sbData
;
2480 target
->bans
= sbData
;
2482 /* Update the user counts for the target channel. */
2487 /* Possible to assert (source->bans == NULL) here. */
2488 source
->bans
= NULL
;
2492 merge_data(struct chanData
*source
, struct chanData
*target
)
2494 /* Use more recent visited and owner-transfer time; use older
2495 * registered time. Bitwise or may_opchan. Use higher max.
2496 * Do not touch last_refresh, ban count or user counts.
2498 if(source
->visited
> target
->visited
)
2499 target
->visited
= source
->visited
;
2500 if(source
->registered
< target
->registered
)
2501 target
->registered
= source
->registered
;
2502 if(source
->ownerTransfer
> target
->ownerTransfer
)
2503 target
->ownerTransfer
= source
->ownerTransfer
;
2504 if(source
->may_opchan
)
2505 target
->may_opchan
= 1;
2506 if(source
->max
> target
->max
)
2507 target
->max
= source
->max
;
2511 merge_channel(struct chanData
*source
, struct chanData
*target
)
2513 merge_users(source
, target
);
2514 merge_bans(source
, target
);
2515 merge_data(source
, target
);
2518 static CHANSERV_FUNC(cmd_merge
)
2520 struct userData
*target_user
;
2521 struct chanNode
*target
;
2522 char reason
[MAXLEN
];
2526 /* Make sure the target channel exists and is registered to the user
2527 performing the command. */
2528 if(!(target
= GetChannel(argv
[1])))
2530 reply("MSG_INVALID_CHANNEL");
2534 if(!target
->channel_info
)
2536 reply("CSMSG_NOT_REGISTERED", target
->name
);
2540 if(IsProtected(channel
->channel_info
))
2542 reply("CSMSG_MERGE_NODELETE");
2546 if(IsSuspended(target
->channel_info
))
2548 reply("CSMSG_MERGE_SUSPENDED");
2552 if(channel
== target
)
2554 reply("CSMSG_MERGE_SELF");
2558 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2559 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2561 reply("CSMSG_MERGE_NOT_OWNER");
2565 /* Merge the channel structures and associated data. */
2566 merge_channel(channel
->channel_info
, target
->channel_info
);
2567 spamserv_cs_move_merge(user
, channel
, target
, 0);
2568 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2569 unregister_channel(channel
->channel_info
, reason
);
2570 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2574 static CHANSERV_FUNC(cmd_opchan
)
2576 struct mod_chanmode change
;
2577 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2579 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2582 channel
->channel_info
->may_opchan
= 0;
2583 mod_chanmode_init(&change
);
2585 change
.args
[0].mode
= MODE_CHANOP
;
2586 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2587 mod_chanmode_announce(chanserv
, channel
, &change
);
2588 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2592 static CHANSERV_FUNC(cmd_adduser
)
2594 struct userData
*actee
;
2595 struct userData
*actor
;
2596 struct handle_info
*handle
;
2597 unsigned short access
;
2601 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2603 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2607 access
= user_level_from_name(argv
[2], UL_OWNER
);
2610 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2614 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2615 if(actor
->access
<= access
)
2617 reply("CSMSG_NO_BUMP_ACCESS");
2621 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2623 // 'kevin must first authenticate with AuthServ.' is sent to user
2624 struct userNode
*unode
;
2625 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2628 if(find_adduser_pending(channel
, unode
)) {
2629 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2632 if(IsInChannel(channel
, unode
)) {
2633 reply("CSMSG_ADDUSER_PENDING");
2634 add_adduser_pending(channel
, unode
, access
);
2635 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2637 /* this results in user must auth AND not in chan errors. too confusing..
2639 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2647 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2649 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2653 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2654 scan_user_presence(actee
, NULL
);
2655 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2659 static CHANSERV_FUNC(cmd_clvl
)
2661 struct handle_info
*handle
;
2662 struct userData
*victim
;
2663 struct userData
*actor
;
2664 unsigned short new_access
;
2665 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2669 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2671 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2674 if(handle
== user
->handle_info
&& !privileged
)
2676 reply("CSMSG_NO_SELF_CLVL");
2680 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2682 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2686 if(actor
->access
<= victim
->access
&& !privileged
)
2688 reply("MSG_USER_OUTRANKED", handle
->handle
);
2692 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2696 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2700 if(new_access
>= actor
->access
&& !privileged
)
2702 reply("CSMSG_NO_BUMP_ACCESS");
2706 victim
->access
= new_access
;
2707 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2711 static CHANSERV_FUNC(cmd_deluser
)
2713 struct handle_info
*handle
;
2714 struct userData
*victim
;
2715 struct userData
*actor
;
2716 unsigned short access
;
2721 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2723 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2726 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2728 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2734 access
= user_level_from_name(argv
[1], UL_OWNER
);
2737 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2740 if(access
!= victim
->access
)
2742 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2748 access
= victim
->access
;
2751 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2753 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2757 chan_name
= strdup(channel
->name
);
2758 del_channel_user(victim
, 1);
2759 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2765 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2767 struct userData
*actor
, *uData
, *next
;
2769 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2771 if(min_access
> max_access
)
2773 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2777 if((actor
->access
<= max_access
) && !IsHelping(user
))
2779 reply("CSMSG_NO_ACCESS");
2783 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2787 if((uData
->access
>= min_access
)
2788 && (uData
->access
<= max_access
)
2789 && match_ircglob(uData
->handle
->handle
, mask
))
2790 del_channel_user(uData
, 1);
2793 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2797 static CHANSERV_FUNC(cmd_mdelowner
)
2799 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2802 static CHANSERV_FUNC(cmd_mdelcoowner
)
2804 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_OWNER
-1, argv
[1], cmd
);
2807 static CHANSERV_FUNC(cmd_mdelmanager
)
2809 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_COOWNER
-1, argv
[1], cmd
);
2812 static CHANSERV_FUNC(cmd_mdelop
)
2814 return cmd_mdel_user(user
, channel
, UL_OP
, UL_MANAGER
-1, argv
[1], cmd
);
2817 static CHANSERV_FUNC(cmd_mdelhalfop
)
2819 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_OP
-1, argv
[1], cmd
);
2822 static CHANSERV_FUNC(cmd_mdelpeon
)
2824 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_HALFOP
-1, argv
[1], cmd
);
2828 static CHANSERV_FUNC(cmd_levels
)
2830 struct helpfile_table tbl
;
2833 tbl
.length
= 6 + 1; // 6 levels
2836 tbl
.contents
= calloc(tbl
.length
,sizeof(tbl
.contents
[0]));
2837 tbl
.contents
[0] = calloc(tbl
.width
,sizeof(tbl
.contents
[0][0]));
2838 tbl
.contents
[0][0] = "Level";
2839 tbl
.contents
[0][1] = "From";
2840 tbl
.contents
[0][2] = "-";
2841 tbl
.contents
[0][3] = "To";
2843 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2844 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OWNER
));
2845 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OWNER
);
2846 tbl
.contents
[ii
][2] = msnprintf(2, " ");
2847 tbl
.contents
[ii
][3] = msnprintf(1, "");
2849 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2850 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_COOWNER
));
2851 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_COOWNER
);
2852 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2853 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OWNER
-1);
2855 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2856 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_MANAGER
));
2857 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_MANAGER
);
2858 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2859 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_COOWNER
-1);
2861 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2862 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_OP
));
2863 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_OP
);
2864 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2865 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_MANAGER
-1);
2867 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2868 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_HALFOP
));
2869 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_HALFOP
);
2870 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2871 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_OP
-1);
2873 tbl
.contents
[++ii
] = calloc(tbl
.width
, sizeof(tbl
.contents
[0][0]));
2874 tbl
.contents
[ii
][0] = strdup(user_level_name_from_level(UL_PEON
));
2875 tbl
.contents
[ii
][1] = msnprintf(4, "%d", UL_PEON
);
2876 tbl
.contents
[ii
][2] = msnprintf(2, "-");
2877 tbl
.contents
[ii
][3] = msnprintf(4, "%d", UL_HALFOP
-1);
2879 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
2883 reply("CSMSG_LEVELS_HEADER");
2884 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
2885 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
2886 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
2887 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
2888 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
2889 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
2896 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2898 struct banData
*bData
, *next
;
2899 char interval
[INTERVALLEN
];
2904 limit
= now
- duration
;
2905 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2909 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2912 del_channel_ban(bData
);
2916 intervalString(interval
, duration
, user
->handle_info
);
2917 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2922 cmd_trim_users(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2924 struct userData
*actor
, *uData
, *next
;
2925 char interval
[INTERVALLEN
];
2929 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2930 if(min_access
> max_access
)
2932 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2936 if((actor
->access
<= max_access
) && !IsHelping(user
))
2938 reply("CSMSG_NO_ACCESS");
2943 limit
= now
- duration
;
2944 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2948 if((uData
->seen
> limit
)
2950 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2953 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2954 || (!max_access
&& (uData
->access
< actor
->access
)))
2956 del_channel_user(uData
, 1);
2964 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2966 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2970 static CHANSERV_FUNC(cmd_trim
)
2972 unsigned long duration
;
2973 unsigned short min_level
, max_level
;
2978 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2979 duration
= ParseInterval(argv
[2]);
2982 reply("CSMSG_CANNOT_TRIM");
2986 if(!irccasecmp(argv
[1], "lamers"))
2988 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
2991 else if(!irccasecmp(argv
[1], "users"))
2993 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
2996 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2998 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
3001 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
3003 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
3008 reply("CSMSG_INVALID_TRIM", argv
[1]);
3013 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3014 to the user. cmd_all takes advantage of this. */
3015 static CHANSERV_FUNC(cmd_up
)
3017 struct mod_chanmode change
;
3018 struct userData
*uData
;
3021 mod_chanmode_init(&change
);
3023 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3024 if(!change
.args
[0].u
.member
)
3027 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3031 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
3035 reply("CSMSG_GODMODE_UP", argv
[0]);
3038 else if(uData
->access
>= UL_OP
)
3040 change
.args
[0].mode
= MODE_CHANOP
;
3041 errmsg
= "CSMSG_ALREADY_OPPED";
3043 else if(uData
->access
>= UL_HALFOP
)
3045 change
.args
[0].mode
= MODE_HALFOP
;
3046 errmsg
= "CSMSG_ALREADY_HALFOPPED";
3048 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
3050 change
.args
[0].mode
= MODE_VOICE
;
3051 errmsg
= "CSMSG_ALREADY_VOICED";
3056 reply("CSMSG_NO_ACCESS");
3059 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
3060 if(!change
.args
[0].mode
)
3063 reply(errmsg
, channel
->name
);
3066 modcmd_chanmode_announce(&change
);
3070 static CHANSERV_FUNC(cmd_down
)
3072 struct mod_chanmode change
;
3074 mod_chanmode_init(&change
);
3076 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3077 if(!change
.args
[0].u
.member
)
3080 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3084 if(!change
.args
[0].u
.member
->modes
)
3087 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3091 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3092 modcmd_chanmode_announce(&change
);
3096 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
)
3098 struct userData
*cList
;
3100 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3102 if(IsSuspended(cList
->channel
)
3103 || IsUserSuspended(cList
)
3104 || !GetUserMode(cList
->channel
->channel
, user
))
3107 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3113 static CHANSERV_FUNC(cmd_upall
)
3115 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3118 static CHANSERV_FUNC(cmd_downall
)
3120 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3123 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3124 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3127 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
)
3129 unsigned int ii
, valid
;
3130 struct userNode
*victim
;
3131 struct mod_chanmode
*change
;
3133 change
= mod_chanmode_alloc(argc
- 1);
3135 for(ii
=valid
=0; ++ii
< argc
; )
3137 if(!(victim
= GetUserH(argv
[ii
])))
3139 change
->args
[valid
].mode
= mode
;
3140 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3141 if(!change
->args
[valid
].u
.member
)
3143 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3148 change
->argc
= valid
;
3149 if(valid
< (argc
-1))
3150 reply("CSMSG_PROCESS_FAILED");
3153 modcmd_chanmode_announce(change
);
3154 reply(action
, channel
->name
);
3156 mod_chanmode_free(change
);
3160 static CHANSERV_FUNC(cmd_op
)
3162 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3165 static CHANSERV_FUNC(cmd_hop
)
3167 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3170 static CHANSERV_FUNC(cmd_deop
)
3172 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3175 static CHANSERV_FUNC(cmd_dehop
)
3177 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3180 static CHANSERV_FUNC(cmd_voice
)
3182 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3185 static CHANSERV_FUNC(cmd_devoice
)
3187 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3191 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3197 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3199 struct modeNode
*mn
= channel
->members
.list
[ii
];
3201 if(IsService(mn
->user
))
3204 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3207 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3211 victims
[(*victimCount
)++] = mn
;
3217 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3219 struct userNode
*victim
;
3220 struct modeNode
**victims
;
3221 unsigned int offset
, n
, victimCount
, duration
= 0;
3222 char *reason
= "Bye.", *ban
, *name
;
3223 char interval
[INTERVALLEN
];
3225 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3226 REQUIRE_PARAMS(offset
);
3229 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3230 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3232 /* Truncate the reason to a length of TOPICLEN, as
3233 the ircd does; however, leave room for an ellipsis
3234 and the kicker's nick. */
3235 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3239 if((victim
= GetUserH(argv
[1])))
3241 victims
= alloca(sizeof(victims
[0]));
3242 victims
[0] = GetUserMode(channel
, victim
);
3243 /* XXX: The comparison with ACTION_KICK is just because all
3244 * other actions can work on users outside the channel, and we
3245 * want to allow those (e.g. unbans) in that case. If we add
3246 * some other ejection action for in-channel users, change
3248 victimCount
= victims
[0] ? 1 : 0;
3250 if(IsService(victim
))
3253 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3257 if((action
== ACTION_KICK
) && !victimCount
)
3260 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3264 if(protect_user(victim
, user
, channel
->channel_info
, false))
3266 // This translates to send_message(user, cmd->parent->bot, ...)
3267 // if user is x3 (ctcp action) cmd is null and segfault.
3269 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3273 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3274 name
= victim
->nick
;
3278 if(!is_ircmask(argv
[1]))
3281 reply("MSG_NICK_UNKNOWN", argv
[1]);
3285 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3287 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3290 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3293 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3294 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3296 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3297 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3298 some creativity, but its not x3's job to be the ban censor anyway. */
3299 if(is_overmask(argv
[1]))
3302 reply("CSMSG_LAME_MASK", argv
[1]);
3306 if((action
== ACTION_KICK
) && (victimCount
== 0))
3309 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3313 name
= ban
= strdup(argv
[1]);
3316 /* Truncate the ban in place if necessary; we must ensure
3317 that 'ban' is a valid ban mask before sanitizing it. */
3318 sanitize_ircmask(ban
);
3320 if(action
& ACTION_ADD_LAMER
)
3322 struct banData
*bData
, *next
;
3324 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3327 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3332 if(action
& ACTION_ADD_TIMED_LAMER
)
3334 duration
= ParseInterval(argv
[2]);
3339 reply("CSMSG_DURATION_TOO_LOW");
3343 else if(duration
> (86400 * 365 * 2))
3346 reply("CSMSG_DURATION_TOO_HIGH");
3353 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3355 if(match_ircglobs(bData
->mask
, ban
))
3357 int exact
= !irccasecmp(bData
->mask
, ban
);
3359 /* The ban is redundant; there is already a ban
3360 with the same effect in place. */
3364 free(bData
->reason
);
3365 bData
->reason
= strdup(reason
);
3366 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3368 reply("CSMSG_REASON_CHANGE", ban
);
3372 if(exact
&& bData
->expires
)
3376 /* If the ban matches an existing one exactly,
3377 extend the expiration time if the provided
3378 duration is longer. */
3379 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3381 bData
->expires
= now
+ duration
;
3392 /* Delete the expiration timeq entry and
3393 requeue if necessary. */
3394 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3397 timeq_add(bData
->expires
, expire_ban
, bData
);
3401 /* automated kickban, dont reply */
3404 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3406 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3412 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3419 if(match_ircglobs(ban
, bData
->mask
))
3421 /* The ban we are adding makes previously existing
3422 bans redundant; silently remove them. */
3423 del_channel_ban(bData
);
3427 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
);
3429 name
= ban
= strdup(bData
->mask
);
3433 /* WHAT DOES THIS DO?? -Rubin */
3434 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3436 extern const char *hidden_host_suffix
;
3437 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3439 unsigned int l1
, l2
;
3442 l2
= strlen(old_name
);
3445 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3447 new_mask
= malloc(MAXLEN
);
3448 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3450 name
= ban
= new_mask
;
3455 if(action
& ACTION_BAN
)
3457 unsigned int exists
;
3458 struct mod_chanmode
*change
;
3460 if(channel
->banlist
.used
>= MAXBANS
)
3463 reply("CSMSG_BANLIST_FULL", channel
->name
);
3468 exists
= ChannelBanExists(channel
, ban
);
3469 change
= mod_chanmode_alloc(victimCount
+ 1);
3470 for(n
= 0; n
< victimCount
; ++n
)
3472 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3473 change
->args
[n
].u
.member
= victims
[n
];
3477 change
->args
[n
].mode
= MODE_BAN
;
3478 change
->args
[n
++].u
.hostmask
= ban
;
3482 modcmd_chanmode_announce(change
);
3484 mod_chanmode_announce(chanserv
, channel
, change
);
3485 mod_chanmode_free(change
);
3487 if(exists
&& (action
== ACTION_BAN
))
3490 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3496 if(action
& ACTION_ADD_LAMER
)
3498 char kick_reason
[MAXLEN
];
3499 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3501 for(n
= 0; n
< victimCount
; n
++) {
3502 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3503 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3507 else if(action
& ACTION_KICK
)
3509 char kick_reason
[MAXLEN
];
3510 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3512 for(n
= 0; n
< victimCount
; n
++) {
3513 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3519 /* No response, since it was automated. */
3521 else if(action
& ACTION_ADD_LAMER
)
3524 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3526 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3528 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3529 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3530 else if(action
& ACTION_BAN
)
3531 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3532 else if(action
& ACTION_KICK
&& victimCount
)
3533 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3539 static CHANSERV_FUNC(cmd_kickban
)
3541 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3544 static CHANSERV_FUNC(cmd_kick
)
3546 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3549 static CHANSERV_FUNC(cmd_ban
)
3551 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3554 static CHANSERV_FUNC(cmd_addlamer
)
3556 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3559 static CHANSERV_FUNC(cmd_addtimedlamer
)
3561 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3564 static struct mod_chanmode
*
3565 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3567 struct mod_chanmode
*change
;
3568 unsigned char *match
;
3569 unsigned int ii
, count
;
3571 match
= alloca(bans
->used
);
3574 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3576 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3577 MATCH_USENICK
| MATCH_VISIBLE
);
3584 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3586 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3593 change
= mod_chanmode_alloc(count
);
3594 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3598 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3599 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3601 assert(count
== change
->argc
);
3605 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3607 unsigned int jj
, ii
, count
;
3609 struct chanData
*channel
;
3611 struct mod_chanmode
*change
;
3613 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3614 /* Walk through every channel */
3615 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3616 switch(channel
->chOpts
[chBanTimeout
])
3618 default: case '0': continue; /* Dont remove bans in this chan */
3619 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3620 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3621 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3622 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3623 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3626 /* First find out how many bans were going to unset */
3627 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3628 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3632 /* At least one ban, so setup a removal */
3633 change
= mod_chanmode_alloc(count
);
3635 /* Walk over every ban in this channel.. */
3636 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3637 bn
= channel
->channel
->banlist
.list
[jj
];
3638 if (bn
->set
< bantimeout
) {
3639 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3641 /* Add this ban to the mode change */
3642 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3643 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3645 /* Pull this ban out of the list */
3646 banList_remove(&(channel
->channel
->banlist
), bn
);
3651 /* Send the modes to IRC */
3652 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3654 /* free memory from strdup above */
3655 for(ii
= 0; ii
< count
; ++ii
)
3656 free((char*)change
->args
[ii
].u
.hostmask
);
3658 mod_chanmode_free(change
);
3661 /* Set this function to run again */
3662 if(chanserv_conf
.ban_timeout_frequency
)
3663 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3668 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3670 struct userNode
*actee
;
3676 /* may want to allow a comma delimited list of users... */
3677 if(!(actee
= GetUserH(argv
[1])))
3679 if(!is_ircmask(argv
[1]))
3681 reply("MSG_NICK_UNKNOWN", argv
[1]);
3685 mask
= strdup(argv
[1]);
3688 /* We don't sanitize the mask here because ircu
3690 if(action
& ACTION_UNBAN
)
3692 struct mod_chanmode
*change
;
3693 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3698 modcmd_chanmode_announce(change
);
3699 for(ii
= 0; ii
< change
->argc
; ++ii
)
3700 free((char*)change
->args
[ii
].u
.hostmask
);
3701 mod_chanmode_free(change
);
3706 if(action
& ACTION_DEL_LAMER
)
3708 struct banData
*ban
, *next
;
3710 ban
= channel
->channel_info
->bans
; /* lamers */
3714 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3717 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3722 del_channel_ban(ban
);
3729 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3731 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3737 static CHANSERV_FUNC(cmd_unban
)
3739 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3742 static CHANSERV_FUNC(cmd_dellamer
)
3744 /* it doesn't necessarily have to remove the channel ban - may want
3745 to make that an option. */
3746 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3749 static CHANSERV_FUNC(cmd_unbanme
)
3751 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3752 long flags
= ACTION_UNBAN
;
3754 /* remove permanent bans if the user has the proper access. */
3755 if(uData
->access
>= UL_MANAGER
)
3756 flags
|= ACTION_DEL_LAMER
;
3758 argv
[1] = user
->nick
;
3759 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3762 static CHANSERV_FUNC(cmd_unbanall
)
3764 struct mod_chanmode
*change
;
3767 if(!channel
->banlist
.used
)
3769 reply("CSMSG_NO_BANS", channel
->name
);
3773 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3774 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3776 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3777 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3779 modcmd_chanmode_announce(change
);
3780 for(ii
= 0; ii
< change
->argc
; ++ii
)
3781 free((char*)change
->args
[ii
].u
.hostmask
);
3782 mod_chanmode_free(change
);
3783 reply("CSMSG_BANS_REMOVED", channel
->name
);
3787 static CHANSERV_FUNC(cmd_open
)
3789 struct mod_chanmode
*change
;
3792 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3794 change
= mod_chanmode_alloc(0);
3795 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3796 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3797 && channel
->channel_info
->modes
.modes_set
)
3798 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3799 modcmd_chanmode_announce(change
);
3800 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3801 for(ii
= 0; ii
< change
->argc
; ++ii
)
3802 free((char*)change
->args
[ii
].u
.hostmask
);
3803 mod_chanmode_free(change
);
3807 static CHANSERV_FUNC(cmd_myaccess
)
3809 static struct string_buffer sbuf
;
3810 struct handle_info
*target_handle
;
3811 struct userData
*uData
;
3814 target_handle
= user
->handle_info
;
3815 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3817 else if(!IsHelping(user
) && target_handle
!= user
->handle_info
)
3819 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3822 if(!target_handle
->channels
)
3824 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3828 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3829 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3831 struct chanData
*cData
= uData
->channel
;
3833 if(uData
->access
> UL_OWNER
)
3835 if(IsProtected(cData
)
3836 && (target_handle
!= user
->handle_info
)
3837 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3840 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3841 if(uData
->flags
== USER_AUTO_OP
)
3842 string_buffer_append(&sbuf
, ',');
3843 if(IsUserSuspended(uData
))
3844 string_buffer_append(&sbuf
, 's');
3845 if(IsUserAutoOp(uData
))
3847 if(uData
->access
>= UL_OP
)
3848 string_buffer_append(&sbuf
, 'o');
3849 else if(uData
->access
>= UL_HALFOP
)
3850 string_buffer_append(&sbuf
, 'h');
3851 else if(uData
->access
>= UL_PEON
)
3852 string_buffer_append(&sbuf
, 'v');
3854 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3855 string_buffer_append(&sbuf
, 'i');
3856 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3857 string_buffer_append(&sbuf
, 'j');
3859 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3861 string_buffer_append_string(&sbuf
, ")]");
3862 string_buffer_append(&sbuf
, '\0');
3863 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3869 static CHANSERV_FUNC(cmd_access
)
3871 struct userNode
*target
;
3872 struct handle_info
*target_handle
;
3873 struct userData
*uData
;
3875 char prefix
[MAXLEN
];
3880 target_handle
= target
->handle_info
;
3882 else if((target
= GetUserH(argv
[1])))
3884 target_handle
= target
->handle_info
;
3886 else if(argv
[1][0] == '*')
3888 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3890 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3896 reply("MSG_NICK_UNKNOWN", argv
[1]);
3900 assert(target
|| target_handle
);
3902 if(target
== chanserv
)
3904 reply("CSMSG_IS_CHANSERV");
3912 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3917 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3920 reply("MSG_AUTHENTICATE");
3926 const char *epithet
= NULL
, *type
= NULL
;
3929 epithet
= chanserv_conf
.irc_operator_epithet
;
3932 else if(IsNetworkHelper(target
))
3934 epithet
= chanserv_conf
.network_helper_epithet
;
3935 type
= "network helper";
3937 else if(IsSupportHelper(target
))
3939 epithet
= chanserv_conf
.support_helper_epithet
;
3940 type
= "support helper";
3944 if(target_handle
->epithet
)
3945 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3947 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3949 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3953 sprintf(prefix
, "%s", target_handle
->handle
);
3956 if(!channel
->channel_info
)
3958 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3962 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3963 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3964 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3966 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3967 /* To prevent possible information leaks, only show infolines
3968 * if the requestor is in the channel or it's their own
3970 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3972 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3974 /* Likewise, only say it's suspended if the user has active
3975 * access in that channel or it's their own entry. */
3976 if(IsUserSuspended(uData
)
3977 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3978 || (user
->handle_info
== uData
->handle
)))
3980 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3985 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3991 /* This is never used...
3993 zoot_list(struct listData *list)
3995 struct userData *uData;
3996 unsigned int start, curr, highest, lowest;
3997 struct helpfile_table tmp_table;
3998 const char **temp, *msg;
4000 if(list->table.length == 1)
4003 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);
4005 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));
4006 msg = user_find_message(list->user, "MSG_NONE");
4007 send_message_type(4, list->user, list->bot, " %s", msg);
4009 tmp_table.width = list->table.width;
4010 tmp_table.flags = list->table.flags;
4011 list->table.contents[0][0] = " ";
4012 highest = list->highest;
4013 if(list->lowest != 0)
4014 lowest = list->lowest;
4015 else if(highest < 100)
4018 lowest = highest - 100;
4019 for(start = curr = 1; curr < list->table.length; )
4021 uData = list->users[curr-1];
4022 list->table.contents[curr++][0] = " ";
4023 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4026 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);
4028 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));
4029 temp = list->table.contents[--start];
4030 list->table.contents[start] = list->table.contents[0];
4031 tmp_table.contents = list->table.contents + start;
4032 tmp_table.length = curr - start;
4033 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4034 list->table.contents[start] = temp;
4036 highest = lowest - 1;
4037 lowest = (highest < 100) ? 0 : (highest - 99);
4044 normal_list(struct listData
*list
)
4048 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
);
4050 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
));
4051 if(list
->table
.length
== 1)
4053 msg
= user_find_message(list
->user
, "MSG_NONE");
4054 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
4057 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
4060 /* if these need changed, uncomment and customize
4062 clean_list(struct listData *list)
4066 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);
4068 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));
4069 if(list->table.length == 1)
4071 msg = user_find_message(list->user, "MSG_NONE");
4072 send_message_type(4, list->user, list->bot, " %s", msg);
4075 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4079 advanced_list(struct listData *list)
4083 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);
4085 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));
4086 if(list->table.length == 1)
4088 msg = user_find_message(list->user, "MSG_NONE");
4089 send_message_type(4, list->user, list->bot, " %s", msg);
4092 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4096 classic_list(struct listData *list)
4100 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4102 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4103 if(list->table.length == 1)
4105 msg = user_find_message(list->user, "MSG_NONE");
4106 send_message_type(4, list->user, list->bot, " %s", msg);
4109 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4114 userData_access_comp(const void *arg_a
, const void *arg_b
)
4116 const struct userData
*a
= *(struct userData
**)arg_a
;
4117 const struct userData
*b
= *(struct userData
**)arg_b
;
4119 if(a
->access
!= b
->access
)
4120 res
= b
->access
- a
->access
;
4122 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4127 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4129 void (*send_list
)(struct listData
*);
4130 struct userData
*uData
;
4131 struct listData lData
;
4132 unsigned int matches
;
4138 lData
.bot
= cmd
->parent
->bot
;
4139 lData
.channel
= channel
;
4140 lData
.lowest
= lowest
;
4141 lData
.highest
= highest
;
4142 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4143 send_list
= normal_list
;
4144 /* What does the following line do exactly?? */
4145 /*(void)zoot_list; ** since it doesn't show user levels */
4148 if(user->handle_info)
4150 switch(user->handle_info->userlist_style)
4152 case HI_STYLE_CLEAN:
4153 send_list = clean_list;
4155 case HI_STYLE_ADVANCED:
4156 send_list = advanced_list;
4158 case HI_STYLE_CLASSIC:
4159 send_list = classic_list;
4161 case HI_STYLE_NORMAL:
4163 send_list = normal_list;
4168 send_list
= normal_list
;
4170 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4172 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4174 if((uData
->access
< lowest
)
4175 || (uData
->access
> highest
)
4176 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4178 lData
.users
[matches
++] = uData
;
4180 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4182 lData
.table
.length
= matches
+1;
4183 lData
.table
.flags
= TABLE_NO_FREE
;
4184 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4186 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4187 lData
.table
.width
= 5; /* with level = 5 */
4189 lData
.table
.width
= 4; /* without = 4 */
4190 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4191 lData
.table
.contents
[0] = ary
;
4192 if(user
->handle_info
) {
4193 switch(user
->handle_info
->userlist_style
) {
4194 case HI_STYLE_CLASSIC
:
4197 case HI_STYLE_ADVANCED
:
4198 ary
[i
++] = "Access";
4201 case HI_STYLE_CLEAN
:
4202 ary
[i
++] = "Access";
4204 case HI_STYLE_NORMAL
:
4206 ary
[i
++] = "Access";
4211 ary
[i
++] = "Access";
4213 ary
[i
++] = "Account";
4214 ary
[i
] = "Last Seen";
4216 ary
[i
++] = "Status";
4217 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4219 struct userData
*uData
= lData
.users
[matches
-1];
4220 char seen
[INTERVALLEN
];
4223 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4224 lData
.table
.contents
[matches
] = ary
;
4225 if(user
->handle_info
) {
4226 switch(user
->handle_info
->userlist_style
) {
4227 case HI_STYLE_CLASSIC
:
4228 ary
[i
++] = strtab(uData
->access
);
4230 case HI_STYLE_ADVANCED
:
4231 ary
[i
++] = user_level_name_from_level(uData
->access
);
4232 ary
[i
++] = strtab(uData
->access
);
4234 case HI_STYLE_CLEAN
:
4235 ary
[i
++] = user_level_name_from_level(uData
->access
);
4237 case HI_STYLE_NORMAL
:
4239 ary
[i
++] = user_level_name_from_level(uData
->access
);
4244 ary
[i
++] = user_level_name_from_level(uData
->access
);
4246 ary
[i
++] = uData
->handle
->handle
;
4249 else if(!uData
->seen
)
4252 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4253 ary
[i
] = strdup(ary
[i
]);
4255 if(IsUserSuspended(uData
))
4256 ary
[i
++] = "Suspended";
4257 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4258 ary
[i
++] = "Vacation";
4260 ary
[i
++] = "Normal";
4263 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4265 /* Free strdup above */
4266 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4267 free(lData
.table
.contents
[matches
]);
4269 free(lData
.table
.contents
[0]);
4270 free(lData
.table
.contents
);
4274 /* Remove this now that debugging is over? or improve it for
4275 * users? Would it be better tied into USERS somehow? -Rubin */
4276 static CHANSERV_FUNC(cmd_pending
)
4278 struct adduserPending
*ap
;
4279 reply("CSMSG_ADDUSER_PENDING_HEADER");
4280 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4282 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4283 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4284 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4288 static CHANSERV_FUNC(cmd_users
)
4290 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4293 static CHANSERV_FUNC(cmd_wlist
)
4295 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4298 static CHANSERV_FUNC(cmd_clist
)
4300 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4303 static CHANSERV_FUNC(cmd_mlist
)
4305 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4308 static CHANSERV_FUNC(cmd_olist
)
4310 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4313 static CHANSERV_FUNC(cmd_hlist
)
4315 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4318 static CHANSERV_FUNC(cmd_plist
)
4320 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4323 static CHANSERV_FUNC(cmd_lamers
)
4325 struct helpfile_table tbl
;
4326 unsigned int matches
= 0, timed
= 0, ii
;
4327 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4328 const char *msg_never
, *triggered
, *expires
;
4329 struct banData
*ban
, **bans
; /* lamers */
4336 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4337 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4340 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4342 if(search
&& !match_ircglobs(search
, ban
->mask
))
4344 bans
[matches
++] = ban
;
4349 tbl
.length
= matches
+ 1;
4350 tbl
.width
= 4 + timed
;
4352 tbl
.flags
= TABLE_NO_FREE
;
4353 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4354 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4355 tbl
.contents
[0][0] = "Mask";
4356 tbl
.contents
[0][1] = "Set By";
4357 tbl
.contents
[0][2] = "Triggered";
4360 tbl
.contents
[0][3] = "Expires";
4361 tbl
.contents
[0][4] = "Reason";
4364 tbl
.contents
[0][3] = "Reason";
4367 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4368 /* reply("MSG_NONE"); */
4369 free(tbl
.contents
[0]);
4374 msg_never
= user_find_message(user
, "MSG_NEVER");
4375 for(ii
= 0; ii
< matches
; )
4381 else if(ban
->expires
)
4382 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4384 expires
= msg_never
;
4387 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4389 triggered
= msg_never
;
4391 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4392 tbl
.contents
[ii
][0] = ban
->mask
;
4393 tbl
.contents
[ii
][1] = ban
->owner
;
4394 tbl
.contents
[ii
][2] = strdup(triggered
);
4397 tbl
.contents
[ii
][3] = strdup(expires
);
4398 tbl
.contents
[ii
][4] = ban
->reason
;
4401 tbl
.contents
[ii
][3] = ban
->reason
;
4403 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4404 /* reply("MSG_MATCH_COUNT", matches); */
4405 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4407 free((char*)tbl
.contents
[ii
][2]);
4409 free((char*)tbl
.contents
[ii
][3]);
4410 free(tbl
.contents
[ii
]);
4412 free(tbl
.contents
[0]);
4419 * return + if the user does NOT have the right to set the topic, and
4420 * the topic is changed.
4423 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4425 struct chanData
*cData
= channel
->channel_info
;
4426 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4428 else if(cData
->topic
)
4429 return irccasecmp(new_topic
, cData
->topic
);
4436 * Makes a givin topic fit into a givin topic mask and returns
4439 * topic_mask - the mask to conform to
4440 * topic - the topic to make conform
4441 * new_topic - the pre-allocated char* to put the new topic into
4443 * modifies: new_topic
4446 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4448 //char *topic_mask = cData->topic_mask;
4450 int pos
=0, starpos
=-1, dpos
=0, len
;
4452 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4459 strcpy(new_topic
, "");
4462 len
= strlen(topic
);
4463 if((dpos
+ len
) > TOPICLEN
)
4464 len
= TOPICLEN
+ 1 - dpos
;
4465 memcpy(new_topic
+dpos
, topic
, len
);
4469 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4470 default: new_topic
[dpos
++] = tchar
; break;
4473 if((dpos
> TOPICLEN
) || tchar
)
4475 strcpy(new_topic
, "");
4478 new_topic
[dpos
] = 0;
4482 static CHANSERV_FUNC(cmd_topic
)
4484 struct chanData
*cData
;
4488 #ifdef WITH_PROTOCOL_P10
4492 cData
= channel
->channel_info
;
4497 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4498 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4499 reply("CSMSG_TOPIC_SET", cData
->topic
);
4503 reply("CSMSG_NO_TOPIC", channel
->name
);
4507 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4508 /* If they say "!topic *", use an empty topic. */
4509 if((topic
[0] == '*') && (topic
[1] == 0))
4512 if(bad_topic(channel
, user
, topic
))
4514 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4519 /* If there is a topicmask set, and the new topic doesnt match, make it */
4520 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4522 char *topic_mask
= cData
->topic_mask
;
4523 char new_topic
[TOPICLEN
+1];
4525 /* make a new topic fitting mask */
4526 conform_topic(topic_mask
, topic
, new_topic
);
4529 /* Topic couldnt fit into mask, was too long */
4530 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4531 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4534 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4536 else /* No mask set, just set the topic */
4537 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4540 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4542 /* Grab the topic and save it as the default topic. */
4544 cData
->topic
= strdup(channel
->topic
);
4550 static CHANSERV_FUNC(cmd_mode
)
4552 struct userData
*uData
;
4553 struct mod_chanmode
*change
;
4558 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4559 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4563 change
= &channel
->channel_info
->modes
;
4564 if(change
->modes_set
|| change
->modes_clear
) {
4565 modcmd_chanmode_announce(change
);
4566 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4568 reply("CSMSG_NO_MODES", channel
->name
);
4572 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4574 base_oplevel
= MAXOPLEVEL
;
4575 else if (uData
->access
>= UL_OWNER
)
4578 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4579 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4583 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4587 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4588 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4591 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4592 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4596 modcmd_chanmode_announce(change
);
4597 mod_chanmode_free(change
);
4598 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4602 static CHANSERV_FUNC(cmd_invite
)
4604 struct userData
*uData
;
4605 struct userNode
*invite
;
4607 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4611 if(!(invite
= GetUserH(argv
[1])))
4613 reply("MSG_NICK_UNKNOWN", argv
[1]);
4620 if(GetUserMode(channel
, invite
))
4622 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4630 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4631 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4634 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4637 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4639 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4640 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4641 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4647 irc_invite(chanserv
, invite
, channel
);
4649 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4654 static CHANSERV_FUNC(cmd_inviteme
)
4656 if(GetUserMode(channel
, user
))
4658 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4661 if(channel
->channel_info
4662 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4664 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4667 irc_invite(cmd
->parent
->bot
, user
, channel
);
4672 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4675 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4677 /* We display things based on two dimensions:
4678 * - Issue time: present or absent
4679 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4680 * (in order of precedence, so something both expired and revoked
4681 * only counts as revoked)
4683 combo
= (suspended
->issued
? 4 : 0)
4684 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4686 case 0: /* no issue time, indefinite expiration */
4687 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4689 case 1: /* no issue time, expires in future */
4690 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4691 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4693 case 2: /* no issue time, expired */
4694 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4695 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4697 case 3: /* no issue time, revoked */
4698 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4699 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4701 case 4: /* issue time set, indefinite expiration */
4702 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4703 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4705 case 5: /* issue time set, expires in future */
4706 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4707 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4708 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4710 case 6: /* issue time set, expired */
4711 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4712 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4713 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4715 case 7: /* issue time set, revoked */
4716 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4717 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4718 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4721 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4727 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4730 const char *fmt
= "%a %b %d %H:%M %Y";
4731 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4733 if(giveownership
->staff_issuer
)
4735 if(giveownership
->reason
)
4736 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4737 giveownership
->target
, giveownership
->target_access
,
4738 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4740 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4741 giveownership
->target
, giveownership
->target_access
,
4742 giveownership
->staff_issuer
, buf
);
4746 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4751 static CHANSERV_FUNC(cmd_info
)
4753 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4754 struct userData
*uData
, *owner
;
4755 struct chanData
*cData
;
4756 struct do_not_register
*dnr
;
4761 cData
= channel
->channel_info
;
4762 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4763 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4766 uData
= GetChannelUser(cData
, user
->handle_info
);
4767 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4769 mod_chanmode_format(&cData
->modes
, modes
);
4770 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4771 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4774 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4778 note
= iter_data(it
);
4779 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4782 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4783 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4786 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4787 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4788 if(owner
->access
== UL_OWNER
)
4789 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4790 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4791 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4792 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4794 privileged
= IsStaff(user
);
4795 /* if(privileged) */
4796 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4797 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4798 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4800 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4801 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4803 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4805 struct suspended
*suspended
;
4806 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4807 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4808 show_suspension_info(cmd
, user
, suspended
);
4810 else if(IsSuspended(cData
))
4812 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4813 show_suspension_info(cmd
, user
, cData
->suspended
);
4815 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4817 struct giveownership
*giveownership
;
4818 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4819 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4820 show_giveownership_info(cmd
, user
, giveownership
);
4822 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4823 reply("CSMSG_CHANNEL_END");
4825 reply("CSMSG_CHANNEL_END_CLEAN");
4829 static CHANSERV_FUNC(cmd_netinfo
)
4831 extern time_t boot_time
;
4832 extern unsigned long burst_length
;
4833 char interval
[INTERVALLEN
];
4835 reply("CSMSG_NETWORK_INFO");
4836 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4837 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4838 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4839 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4840 reply("CSMSG_NETWORK_LAMERS", banCount
);
4841 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4842 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4843 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4848 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4850 struct helpfile_table table
;
4852 struct userNode
*user
;
4857 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4858 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4859 for(nn
=0; nn
<list
->used
; nn
++)
4861 user
= list
->list
[nn
];
4862 if(user
->modes
& skip_flags
)
4866 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4869 nick
= alloca(strlen(user
->nick
)+3);
4870 sprintf(nick
, "(%s)", user
->nick
);
4874 table
.contents
[table
.length
][0] = nick
;
4877 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4880 static CHANSERV_FUNC(cmd_ircops
)
4882 reply("CSMSG_STAFF_OPERS");
4883 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4887 static CHANSERV_FUNC(cmd_helpers
)
4889 reply("CSMSG_STAFF_HELPERS");
4890 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4894 static CHANSERV_FUNC(cmd_staff
)
4896 reply("CSMSG_NETWORK_STAFF");
4897 cmd_ircops(CSFUNC_ARGS
);
4898 cmd_helpers(CSFUNC_ARGS
);
4902 static CHANSERV_FUNC(cmd_peek
)
4904 struct modeNode
*mn
;
4905 char modes
[MODELEN
];
4907 struct helpfile_table table
;
4909 irc_make_chanmode(channel
, modes
);
4911 reply("CSMSG_PEEK_INFO", channel
->name
);
4912 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4914 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4915 reply("CSMSG_PEEK_MODES", modes
);
4916 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4920 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4921 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4922 for(n
= 0; n
< channel
->members
.used
; n
++)
4924 mn
= channel
->members
.list
[n
];
4925 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4927 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4928 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4933 reply("CSMSG_PEEK_OPS");
4934 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4937 reply("CSMSG_PEEK_NO_OPS");
4938 reply("CSMSG_PEEK_END");
4942 static MODCMD_FUNC(cmd_wipeinfo
)
4944 struct handle_info
*victim
;
4945 struct userData
*ud
, *actor
;
4948 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4949 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4951 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4953 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4956 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4958 reply("MSG_USER_OUTRANKED", victim
->handle
);
4964 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4969 resync_channel(struct chanNode
*channel
)
4971 struct mod_chanmode
*changes
;
4972 struct chanData
*cData
= channel
->channel_info
;
4973 unsigned int ii
, used
;
4975 /* 6 = worst case -ovh+ovh on everyone */
4976 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4977 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4979 struct modeNode
*mn
= channel
->members
.list
[ii
];
4980 struct userData
*uData
;
4982 if(IsService(mn
->user
))
4986 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4988 /* If the channel is in no-mode mode, de-mode EVERYONE */
4989 if(cData
->chOpts
[chAutomode
] == 'n')
4993 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4994 changes
->args
[used
++].u
.member
= mn
;
4997 else /* Give various userlevels their modes.. */
4999 if(uData
&& uData
->access
>= UL_OP
)
5001 if(!(mn
->modes
& MODE_CHANOP
))
5003 changes
->args
[used
].mode
= MODE_CHANOP
;
5004 changes
->args
[used
++].u
.member
= mn
;
5007 else if(uData
&& uData
->access
>= UL_HALFOP
)
5009 if(mn
->modes
& MODE_CHANOP
)
5011 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5012 changes
->args
[used
++].u
.member
= mn
;
5014 if(!(mn
->modes
& MODE_HALFOP
))
5016 changes
->args
[used
].mode
= MODE_HALFOP
;
5017 changes
->args
[used
++].u
.member
= mn
;
5020 else if(uData
&& uData
->access
>= UL_PEON
)
5022 if(mn
->modes
& MODE_CHANOP
)
5024 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
5025 changes
->args
[used
++].u
.member
= mn
;
5027 if(mn
->modes
& MODE_HALFOP
)
5029 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
5030 changes
->args
[used
++].u
.member
= mn
;
5032 /* Don't voice peons if were in mode m */
5033 if( cData
->chOpts
[chAutomode
] == 'm')
5035 if(mn
->modes
& MODE_VOICE
)
5037 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
5038 changes
->args
[used
++].u
.member
= mn
;
5041 /* otherwise, make user they do have voice */
5042 else if(!(mn
->modes
& MODE_VOICE
))
5044 changes
->args
[used
].mode
= MODE_VOICE
;
5045 changes
->args
[used
++].u
.member
= mn
;
5048 else /* They arnt on the userlist.. */
5050 /* If we voice everyone, but they dont.. */
5051 if(cData
->chOpts
[chAutomode
] == 'v')
5053 /* Remove anything except v */
5054 if(mn
->modes
& ~MODE_VOICE
)
5056 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
5057 changes
->args
[used
++].u
.member
= mn
;
5060 if(!(mn
->modes
& MODE_VOICE
))
5062 changes
->args
[used
].mode
= MODE_VOICE
;
5063 changes
->args
[used
++].u
.member
= mn
;
5066 /* If we hop everyone, but they dont.. */
5067 else if(cData
->chOpts
[chAutomode
] == 'h')
5069 /* Remove anything except h */
5070 if(mn
->modes
& ~MODE_HALFOP
)
5072 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5073 changes
->args
[used
++].u
.member
= mn
;
5076 if(!(mn
->modes
& MODE_HALFOP
))
5078 changes
->args
[used
].mode
= MODE_HALFOP
;
5079 changes
->args
[used
++].u
.member
= mn
;
5082 /* If we op everyone, but they dont.. */
5083 else if(cData
->chOpts
[chAutomode
] == 'o')
5085 /* Remove anything except h */
5086 if(mn
->modes
& ~MODE_CHANOP
)
5088 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5089 changes
->args
[used
++].u
.member
= mn
;
5092 if(!(mn
->modes
& MODE_CHANOP
))
5094 changes
->args
[used
].mode
= MODE_CHANOP
;
5095 changes
->args
[used
++].u
.member
= mn
;
5098 /* they have no excuse for having modes, de-everything them */
5103 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5104 changes
->args
[used
++].u
.member
= mn
;
5110 changes
->argc
= used
;
5111 mod_chanmode_announce(chanserv
, channel
, changes
);
5112 mod_chanmode_free(changes
);
5115 static CHANSERV_FUNC(cmd_resync
)
5117 resync_channel(channel
);
5118 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5122 static CHANSERV_FUNC(cmd_seen
)
5124 struct userData
*uData
;
5125 struct handle_info
*handle
;
5126 char seen
[INTERVALLEN
];
5130 if(!irccasecmp(argv
[1], chanserv
->nick
))
5132 reply("CSMSG_IS_CHANSERV");
5136 if(!(handle
= get_handle_info(argv
[1])))
5138 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5142 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5144 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5149 reply("CSMSG_USER_PRESENT", handle
->handle
);
5150 else if(uData
->seen
)
5151 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5153 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5155 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5156 reply("CSMSG_USER_VACATION", handle
->handle
);
5161 static MODCMD_FUNC(cmd_names
)
5163 struct userNode
*targ
;
5164 struct userData
*targData
;
5165 unsigned int ii
, pos
;
5168 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5170 targ
= channel
->members
.list
[ii
]->user
;
5171 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5174 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5177 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5181 if(IsUserSuspended(targData
))
5183 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5186 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5187 reply("CSMSG_END_NAMES", channel
->name
);
5192 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5194 switch(ntype
->visible_type
)
5196 case NOTE_VIS_ALL
: return 1;
5197 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5198 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5203 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5205 struct userData
*uData
;
5207 switch(ntype
->set_access_type
)
5209 case NOTE_SET_CHANNEL_ACCESS
:
5210 if(!user
->handle_info
)
5212 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5214 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5215 case NOTE_SET_CHANNEL_SETTER
:
5216 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5217 case NOTE_SET_PRIVILEGED
: default:
5218 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5222 static CHANSERV_FUNC(cmd_note
)
5224 struct chanData
*cData
;
5226 struct note_type
*ntype
;
5228 cData
= channel
->channel_info
;
5231 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5235 /* If no arguments, show all visible notes for the channel. */
5241 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5243 note
= iter_data(it
);
5244 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5247 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5248 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5251 reply("CSMSG_NOTELIST_END", channel
->name
);
5253 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5255 /* If one argument, show the named note. */
5258 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5259 && note_type_visible_to_user(cData
, note
->type
, user
))
5261 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5263 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5264 && note_type_visible_to_user(NULL
, ntype
, user
))
5266 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5271 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5275 /* Assume they're trying to set a note. */
5279 ntype
= dict_find(note_types
, argv
[1], NULL
);
5282 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5285 else if(note_type_settable_by_user(channel
, ntype
, user
))
5287 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5288 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5289 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5290 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5291 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5293 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5295 /* The note is viewable to staff only, so return 0
5296 to keep the invocation from getting logged (or
5297 regular users can see it in !events). */
5303 reply("CSMSG_NO_ACCESS");
5310 static CHANSERV_FUNC(cmd_delnote
)
5315 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5316 || !note_type_settable_by_user(channel
, note
->type
, user
))
5318 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5321 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5322 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5326 static CHANSERV_FUNC(cmd_last
)
5332 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5334 if(numoflines
< 1 || numoflines
> 200)
5336 reply("CSMSG_LAST_INVALID");
5339 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5343 static CHANSERV_FUNC(cmd_events
)
5345 struct logSearch discrim
;
5346 struct logReport report
;
5347 unsigned int matches
, limit
;
5349 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5350 if(limit
< 1 || limit
> 200)
5353 memset(&discrim
, 0, sizeof(discrim
));
5354 discrim
.masks
.bot
= chanserv
;
5355 discrim
.masks
.channel_name
= channel
->name
;
5357 discrim
.masks
.command
= argv
[2];
5358 discrim
.limit
= limit
;
5359 discrim
.max_time
= INT_MAX
;
5360 discrim
.severities
= 1 << LOG_COMMAND
;
5361 report
.reporter
= chanserv
;
5363 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5364 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5366 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5368 reply("MSG_MATCH_COUNT", matches
);
5370 reply("MSG_NO_MATCHES");
5374 static CHANSERV_FUNC(cmd_say
)
5380 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5381 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5383 else if(GetUserH(argv
[1]))
5386 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5387 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5391 reply("MSG_NOT_TARGET_NAME");
5397 static CHANSERV_FUNC(cmd_emote
)
5403 /* CTCP is so annoying. */
5404 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5405 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5407 else if(GetUserH(argv
[1]))
5409 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5410 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5414 reply("MSG_NOT_TARGET_NAME");
5420 struct channelList
*
5421 chanserv_support_channels(void)
5423 return &chanserv_conf
.support_channels
;
5426 static CHANSERV_FUNC(cmd_expire
)
5428 int channel_count
= registered_channels
;
5429 expire_channels(NULL
);
5430 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5435 chanserv_expire_suspension(void *data
)
5437 struct suspended
*suspended
= data
;
5438 struct chanNode
*channel
;
5440 if(!suspended
->expires
|| (now
< suspended
->expires
))
5441 suspended
->revoked
= now
;
5442 channel
= suspended
->cData
->channel
;
5443 suspended
->cData
->channel
= channel
;
5444 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5445 if(!IsOffChannel(suspended
->cData
))
5447 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5448 ss_cs_join_channel(channel
, 1);
5452 static CHANSERV_FUNC(cmd_csuspend
)
5454 struct suspended
*suspended
;
5455 char reason
[MAXLEN
];
5456 time_t expiry
, duration
;
5457 struct userData
*uData
;
5461 if(IsProtected(channel
->channel_info
))
5463 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5467 if(argv
[1][0] == '!')
5469 else if(IsSuspended(channel
->channel_info
))
5471 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5472 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5476 if(!strcmp(argv
[1], "0"))
5478 else if((duration
= ParseInterval(argv
[1])))
5479 expiry
= now
+ duration
;
5482 reply("MSG_INVALID_DURATION", argv
[1]);
5486 unsplit_string(argv
+ 2, argc
- 2, reason
);
5488 suspended
= calloc(1, sizeof(*suspended
));
5489 suspended
->revoked
= 0;
5490 suspended
->issued
= now
;
5491 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5492 suspended
->expires
= expiry
;
5493 suspended
->reason
= strdup(reason
);
5494 suspended
->cData
= channel
->channel_info
;
5495 suspended
->previous
= suspended
->cData
->suspended
;
5496 suspended
->cData
->suspended
= suspended
;
5498 if(suspended
->expires
)
5499 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5501 if(IsSuspended(channel
->channel_info
))
5503 suspended
->previous
->revoked
= now
;
5504 if(suspended
->previous
->expires
)
5505 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5507 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5508 channel
->name
, suspended
->suspender
);
5512 /* Mark all users in channel as absent. */
5513 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5522 /* Mark the channel as suspended, then part. */
5523 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5524 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5525 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5526 reply("CSMSG_SUSPENDED", channel
->name
);
5527 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5528 channel
->name
, suspended
->suspender
);
5533 static CHANSERV_FUNC(cmd_cunsuspend
)
5535 struct suspended
*suspended
;
5537 if(!IsSuspended(channel
->channel_info
))
5539 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5543 suspended
= channel
->channel_info
->suspended
;
5545 /* Expire the suspension and join ChanServ to the channel. */
5546 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5547 chanserv_expire_suspension(suspended
);
5548 reply("CSMSG_UNSUSPENDED", channel
->name
);
5549 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5550 channel
->name
, user
->handle_info
->handle
);
5554 typedef struct chanservSearch
5562 unsigned long flags
;
5566 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5569 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5574 search
= malloc(sizeof(struct chanservSearch
));
5575 memset(search
, 0, sizeof(*search
));
5578 for(i
= 0; i
< argc
; i
++)
5580 /* Assume all criteria require arguments. */
5583 reply("MSG_MISSING_PARAMS", argv
[i
]);
5587 if(!irccasecmp(argv
[i
], "name"))
5588 search
->name
= argv
[++i
];
5589 else if(!irccasecmp(argv
[i
], "registrar"))
5590 search
->registrar
= argv
[++i
];
5591 else if(!irccasecmp(argv
[i
], "unvisited"))
5592 search
->unvisited
= ParseInterval(argv
[++i
]);
5593 else if(!irccasecmp(argv
[i
], "registered"))
5594 search
->registered
= ParseInterval(argv
[++i
]);
5595 else if(!irccasecmp(argv
[i
], "flags"))
5598 if(!irccasecmp(argv
[i
], "nodelete"))
5599 search
->flags
|= CHANNEL_NODELETE
;
5600 else if(!irccasecmp(argv
[i
], "suspended"))
5601 search
->flags
|= CHANNEL_SUSPENDED
;
5604 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5608 else if(!irccasecmp(argv
[i
], "limit"))
5609 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5612 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5617 if(search
->name
&& !strcmp(search
->name
, "*"))
5619 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5620 search
->registrar
= 0;
5629 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5631 const char *name
= channel
->channel
->name
;
5632 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5633 (search
->registrar
&& !channel
->registrar
) ||
5634 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5635 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5636 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5637 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5644 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5646 struct chanData
*channel
;
5647 unsigned int matches
= 0;
5649 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5651 if(!chanserv_channel_match(channel
, search
))
5661 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5666 search_print(struct chanData
*channel
, void *data
)
5668 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5671 static CHANSERV_FUNC(cmd_search
)
5674 unsigned int matches
;
5675 channel_search_func action
;
5679 if(!irccasecmp(argv
[1], "count"))
5680 action
= search_count
;
5681 else if(!irccasecmp(argv
[1], "print"))
5682 action
= search_print
;
5685 reply("CSMSG_ACTION_INVALID", argv
[1]);
5689 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5693 if(action
== search_count
)
5694 search
->limit
= INT_MAX
;
5696 if(action
== search_print
)
5698 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5699 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5703 matches
= chanserv_channel_search(search
, action
, user
);
5706 reply("MSG_MATCH_COUNT", matches
);
5708 reply("MSG_NO_MATCHES");
5714 static CHANSERV_FUNC(cmd_unvisited
)
5716 struct chanData
*cData
;
5717 time_t interval
= chanserv_conf
.channel_expire_delay
;
5718 char buffer
[INTERVALLEN
];
5719 unsigned int limit
= 25, matches
= 0;
5723 interval
= ParseInterval(argv
[1]);
5725 limit
= atoi(argv
[2]);
5728 intervalString(buffer
, interval
, user
->handle_info
);
5729 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5731 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5733 if((now
- cData
->visited
) < interval
)
5736 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5737 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5744 static MODCMD_FUNC(chan_opt_defaulttopic
)
5750 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5752 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5756 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5758 free(channel
->channel_info
->topic
);
5759 if(topic
[0] == '*' && topic
[1] == 0)
5761 topic
= channel
->channel_info
->topic
= NULL
;
5765 topic
= channel
->channel_info
->topic
= strdup(topic
);
5766 if(channel
->channel_info
->topic_mask
5767 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5768 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5770 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5773 if(channel
->channel_info
->topic
)
5774 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5776 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5780 static MODCMD_FUNC(chan_opt_topicmask
)
5784 struct chanData
*cData
= channel
->channel_info
;
5787 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5789 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5793 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5795 if(cData
->topic_mask
)
5796 free(cData
->topic_mask
);
5797 if(mask
[0] == '*' && mask
[1] == 0)
5799 cData
->topic_mask
= 0;
5803 cData
->topic_mask
= strdup(mask
);
5805 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5806 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5807 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5811 if(channel
->channel_info
->topic_mask
)
5812 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5814 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5818 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5822 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5826 if(greeting
[0] == '*' && greeting
[1] == 0)
5830 unsigned int length
= strlen(greeting
);
5831 if(length
> chanserv_conf
.greeting_length
)
5833 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5836 *data
= strdup(greeting
);
5845 reply(name
, user_find_message(user
, "MSG_NONE"));
5849 static MODCMD_FUNC(chan_opt_greeting
)
5851 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5854 static MODCMD_FUNC(chan_opt_usergreeting
)
5856 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5859 static MODCMD_FUNC(chan_opt_modes
)
5861 struct mod_chanmode
*new_modes
;
5862 char modes
[MODELEN
];
5866 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5867 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5871 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5873 reply("CSMSG_NO_ACCESS");
5876 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5878 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5880 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5882 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5885 else if(new_modes
->argc
> 1)
5887 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5888 mod_chanmode_free(new_modes
);
5893 channel
->channel_info
->modes
= *new_modes
;
5894 modcmd_chanmode_announce(new_modes
);
5895 mod_chanmode_free(new_modes
);
5899 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5901 reply("CSMSG_SET_MODES", modes
);
5903 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5907 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5909 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5911 struct chanData
*cData
= channel
->channel_info
;
5916 /* Set flag according to value. */
5917 if(enabled_string(argv
[1]))
5919 cData
->flags
|= mask
;
5922 else if(disabled_string(argv
[1]))
5924 cData
->flags
&= ~mask
;
5929 reply("MSG_INVALID_BINARY", argv
[1]);
5935 /* Find current option value. */
5936 value
= (cData
->flags
& mask
) ? 1 : 0;
5940 reply(name
, user_find_message(user
, "MSG_ON"));
5942 reply(name
, user_find_message(user
, "MSG_OFF"));
5946 static MODCMD_FUNC(chan_opt_nodelete
)
5948 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5950 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5954 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5957 static MODCMD_FUNC(chan_opt_dynlimit
)
5959 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5962 static MODCMD_FUNC(chan_opt_offchannel
)
5964 struct chanData
*cData
= channel
->channel_info
;
5969 /* Set flag according to value. */
5970 if(enabled_string(argv
[1]))
5972 if(!IsOffChannel(cData
))
5973 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5974 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5977 else if(disabled_string(argv
[1]))
5979 if(IsOffChannel(cData
))
5981 struct mod_chanmode change
;
5982 mod_chanmode_init(&change
);
5984 change
.args
[0].mode
= MODE_CHANOP
;
5985 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5986 mod_chanmode_announce(chanserv
, channel
, &change
);
5988 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5993 reply("MSG_INVALID_BINARY", argv
[1]);
5999 /* Find current option value. */
6000 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
6004 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
6006 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
6010 static MODCMD_FUNC(chan_opt_defaults
)
6012 struct userData
*uData
;
6013 struct chanData
*cData
;
6014 const char *confirm
;
6015 enum levelOption lvlOpt
;
6016 enum charOption chOpt
;
6018 cData
= channel
->channel_info
;
6019 uData
= GetChannelUser(cData
, user
->handle_info
);
6020 if(!uData
|| (uData
->access
< UL_OWNER
))
6022 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
6025 confirm
= make_confirmation_string(uData
);
6026 if((argc
< 2) || strcmp(argv
[1], confirm
))
6028 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
6031 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
6032 cData
->modes
= chanserv_conf
.default_modes
;
6033 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6034 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6035 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6036 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
6037 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
6042 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6044 struct chanData
*cData
= channel
->channel_info
;
6045 struct userData
*uData
;
6046 unsigned short value
;
6050 if(!check_user_level(channel
, user
, option
, 1, 1))
6052 reply("CSMSG_CANNOT_SET");
6055 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
6056 if(!value
&& strcmp(argv
[1], "0"))
6058 reply("CSMSG_INVALID_ACCESS", argv
[1]);
6061 uData
= GetChannelUser(cData
, user
->handle_info
);
6062 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
6064 reply("CSMSG_BAD_SETLEVEL");
6070 /* This test only applies to owners, since non-owners
6071 * trying to set an option to above their level get caught
6072 * by the CSMSG_BAD_SETLEVEL test above.
6074 if(value
> uData
->access
)
6076 reply("CSMSG_BAD_SETTERS");
6083 cData
->lvlOpts
[option
] = value
;
6085 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6089 static MODCMD_FUNC(chan_opt_enfops
)
6091 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6094 static MODCMD_FUNC(chan_opt_enfhalfops
)
6096 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6098 static MODCMD_FUNC(chan_opt_enfmodes
)
6100 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6103 static MODCMD_FUNC(chan_opt_enftopic
)
6105 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6108 static MODCMD_FUNC(chan_opt_pubcmd
)
6110 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6113 static MODCMD_FUNC(chan_opt_setters
)
6115 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6118 static MODCMD_FUNC(chan_opt_userinfo
)
6120 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6123 static MODCMD_FUNC(chan_opt_topicsnarf
)
6125 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6128 static MODCMD_FUNC(chan_opt_inviteme
)
6130 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6133 /* TODO: Make look like this when no args are
6135 * -X3- -------------------------------
6136 * -X3- BanTimeout: Bans are removed:
6137 * -X3- ----- * indicates current -----
6138 * -X3- 0: [*] Never.
6139 * -X3- 1: [ ] After 10 minutes.
6140 * -X3- 2: [ ] After 2 hours.
6141 * -X3- 3: [ ] After 4 hours.
6142 * -X3- 4: [ ] After 24 hours.
6143 * -X3- 5: [ ] After one week.
6144 * -X3- ------------- End -------------
6147 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6149 struct chanData
*cData
= channel
->channel_info
;
6150 int count
= charOptions
[option
].count
, index
;
6154 index
= atoi(argv
[1]);
6156 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6158 reply("CSMSG_INVALID_NUMERIC", index
);
6159 /* Show possible values. */
6160 for(index
= 0; index
< count
; index
++)
6161 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6165 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6169 /* Find current option value. */
6172 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6176 /* Somehow, the option value is corrupt; reset it to the default. */
6177 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6182 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6186 static MODCMD_FUNC(chan_opt_automode
)
6188 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6191 static MODCMD_FUNC(chan_opt_protect
)
6193 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6196 static MODCMD_FUNC(chan_opt_toys
)
6198 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6201 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6203 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6206 static MODCMD_FUNC(chan_opt_bantimeout
)
6208 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6211 static MODCMD_FUNC(chan_opt_topicrefresh
)
6213 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6216 static MODCMD_FUNC(chan_opt_resync
)
6218 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6221 static struct svccmd_list set_shows_list
;
6224 handle_svccmd_unbind(struct svccmd
*target
) {
6226 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6227 if(target
== set_shows_list
.list
[ii
])
6228 set_shows_list
.used
= 0;
6231 static CHANSERV_FUNC(cmd_set
)
6233 struct svccmd
*subcmd
;
6237 /* Check if we need to (re-)initialize set_shows_list. */
6238 if(!set_shows_list
.used
)
6240 if(!set_shows_list
.size
)
6242 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6243 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6245 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6247 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6248 sprintf(buf
, "%s %s", argv
[0], name
);
6249 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6252 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6255 svccmd_list_append(&set_shows_list
, subcmd
);
6261 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6262 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6264 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6266 subcmd
= set_shows_list
.list
[ii
];
6267 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6269 reply("CSMSG_CHANNEL_OPTIONS_END");
6273 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6274 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6277 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6280 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6282 reply("CSMSG_NO_ACCESS");
6286 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6290 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6292 struct userData
*uData
;
6294 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6297 reply("CSMSG_NOT_USER", channel
->name
);
6303 /* Just show current option value. */
6305 else if(enabled_string(argv
[1]))
6307 uData
->flags
|= mask
;
6309 else if(disabled_string(argv
[1]))
6311 uData
->flags
&= ~mask
;
6315 reply("MSG_INVALID_BINARY", argv
[1]);
6319 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6323 static MODCMD_FUNC(user_opt_autoop
)
6325 struct userData
*uData
;
6327 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6330 reply("CSMSG_NOT_USER", channel
->name
);
6333 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6334 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6336 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6339 static MODCMD_FUNC(user_opt_autoinvite
)
6341 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6344 static MODCMD_FUNC(user_opt_autojoin
)
6346 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6349 static MODCMD_FUNC(user_opt_info
)
6351 struct userData
*uData
;
6354 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6358 /* If they got past the command restrictions (which require access)
6359 * but fail this test, we have some fool with security override on.
6361 reply("CSMSG_NOT_USER", channel
->name
);
6368 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6369 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6371 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6374 bp
= strcspn(infoline
, "\001");
6377 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6382 if(infoline
[0] == '*' && infoline
[1] == 0)
6385 uData
->info
= strdup(infoline
);
6388 reply("CSMSG_USET_INFO", uData
->info
);
6390 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6394 struct svccmd_list uset_shows_list
;
6396 static CHANSERV_FUNC(cmd_uset
)
6398 struct svccmd
*subcmd
;
6402 /* Check if we need to (re-)initialize uset_shows_list. */
6403 if(!uset_shows_list
.used
)
6407 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6410 if(!uset_shows_list
.size
)
6412 uset_shows_list
.size
= ArrayLength(options
);
6413 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6415 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6417 const char *name
= options
[ii
];
6418 sprintf(buf
, "%s %s", argv
[0], name
);
6419 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6422 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6425 svccmd_list_append(&uset_shows_list
, subcmd
);
6431 /* Do this so options are presented in a consistent order. */
6432 reply("CSMSG_USER_OPTIONS");
6433 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6434 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6438 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6439 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6442 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6446 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6449 static CHANSERV_FUNC(cmd_giveownership
)
6451 struct handle_info
*new_owner_hi
;
6452 struct userData
*new_owner
, *curr_user
;
6453 struct chanData
*cData
= channel
->channel_info
;
6454 struct do_not_register
*dnr
;
6455 struct giveownership
*giveownership
;
6456 unsigned int force
, override
;
6457 unsigned short co_access
, new_owner_old_access
;
6458 char transfer_reason
[MAXLEN
];
6461 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6462 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6464 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6465 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6466 && (uData
->access
> 500)
6467 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6468 || uData
->access
< 500));
6471 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6473 struct userData
*owner
= NULL
;
6474 for(curr_user
= channel
->channel_info
->users
;
6476 curr_user
= curr_user
->next
)
6478 if(curr_user
->access
!= UL_OWNER
)
6482 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6489 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6491 char delay
[INTERVALLEN
];
6492 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6493 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6497 reply("CSMSG_NO_OWNER", channel
->name
);
6500 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6502 if(new_owner_hi
== user
->handle_info
)
6504 reply("CSMSG_NO_TRANSFER_SELF");
6507 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6512 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6516 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6520 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6522 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6525 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6526 if(!IsHelping(user
))
6527 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6529 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6533 new_owner_old_access
= new_owner
->access
;
6534 if(new_owner
->access
>= UL_COOWNER
)
6535 co_access
= new_owner
->access
;
6537 co_access
= UL_COOWNER
;
6538 new_owner
->access
= UL_OWNER
;
6540 curr_user
->access
= co_access
;
6541 cData
->ownerTransfer
= now
;
6543 giveownership
= calloc(1, sizeof(*giveownership
));
6544 giveownership
->issued
= now
;
6545 giveownership
->old_owner
= curr_user
->handle
->handle
;
6546 giveownership
->target
= new_owner_hi
->handle
;
6547 giveownership
->target_access
= new_owner_old_access
;
6550 if(argc
> (2 + force
))
6552 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6553 giveownership
->reason
= strdup(transfer_reason
);
6555 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6558 giveownership
->previous
= channel
->channel_info
->giveownership
;
6559 channel
->channel_info
->giveownership
= giveownership
;
6561 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6562 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6563 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6568 chanserv_expire_user_suspension(void *data
)
6570 struct userData
*target
= data
;
6572 target
->expires
= 0;
6573 target
->flags
&= ~USER_SUSPENDED
;
6576 static CHANSERV_FUNC(cmd_suspend
)
6578 struct handle_info
*hi
;
6579 struct userData
*self
, *target
;
6583 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6584 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6585 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6587 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6590 if(target
->access
>= self
->access
)
6592 reply("MSG_USER_OUTRANKED", hi
->handle
);
6595 if(target
->flags
& USER_SUSPENDED
)
6597 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6602 target
->present
= 0;
6605 if(!strcmp(argv
[2], "0"))
6609 unsigned int duration
;
6610 if(!(duration
= ParseInterval(argv
[2])))
6612 reply("MSG_INVALID_DURATION", argv
[2]);
6615 expiry
= now
+ duration
;
6618 target
->expires
= expiry
;
6621 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6623 target
->flags
|= USER_SUSPENDED
;
6624 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6628 static CHANSERV_FUNC(cmd_unsuspend
)
6630 struct handle_info
*hi
;
6631 struct userData
*self
, *target
;
6634 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6635 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6636 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6638 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6641 if(target
->access
>= self
->access
)
6643 reply("MSG_USER_OUTRANKED", hi
->handle
);
6646 if(!(target
->flags
& USER_SUSPENDED
))
6648 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6651 target
->flags
&= ~USER_SUSPENDED
;
6652 scan_user_presence(target
, NULL
);
6653 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6654 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6658 static MODCMD_FUNC(cmd_deleteme
)
6660 struct handle_info
*hi
;
6661 struct userData
*target
;
6662 const char *confirm_string
;
6663 unsigned short access
;
6666 hi
= user
->handle_info
;
6667 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6669 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6672 if(target
->access
== UL_OWNER
)
6674 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6677 confirm_string
= make_confirmation_string(target
);
6678 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6680 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6683 access
= target
->access
;
6684 channel_name
= strdup(channel
->name
);
6685 del_channel_user(target
, 1);
6686 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6692 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6694 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6695 struct chanData
*cData
;
6698 for(cData
= channelList
; cData
; cData
= cData
->next
)
6700 if(IsSuspended(cData
))
6702 opt
= cData
->chOpts
[chTopicRefresh
];
6705 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6708 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6709 cData
->last_refresh
= refresh_num
;
6711 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6715 chanserv_auto_resync(UNUSED_ARG(void *data
))
6717 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6718 struct chanData
*cData
;
6721 for(cData
= channelList
; cData
; cData
= cData
->next
)
6723 if(IsSuspended(cData
)) continue;
6724 opt
= cData
->chOpts
[chResync
];
6725 if(opt
== 'n') continue;
6726 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6727 resync_channel(cData
->channel
);
6728 cData
->last_resync
= refresh_num
;
6730 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6733 static CHANSERV_FUNC(cmd_unf
)
6737 char response
[MAXLEN
];
6738 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6739 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6740 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6743 reply("CSMSG_UNF_RESPONSE");
6747 static CHANSERV_FUNC(cmd_ping
)
6751 char response
[MAXLEN
];
6752 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6753 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6754 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6757 reply("CSMSG_PING_RESPONSE");
6761 static CHANSERV_FUNC(cmd_wut
)
6765 char response
[MAXLEN
];
6766 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6767 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6768 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6771 reply("CSMSG_WUT_RESPONSE");
6776 static CHANSERV_FUNC(cmd_8ball
)
6778 unsigned int i
, j
, accum
;
6783 for(i
=1; i
<argc
; i
++)
6784 for(j
=0; argv
[i
][j
]; j
++)
6785 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6786 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6789 char response
[MAXLEN
];
6790 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6791 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6794 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6798 #else /* Use cool 8ball instead */
6800 void eightball(char *outcome
, int method
, unsigned int seed
)
6804 #define NUMOFCOLORS 18
6805 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6806 "white", "black", "grey", "brown",
6807 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6808 "fuchsia","turquoise","magenta", "cyan"};
6809 #define NUMOFLOCATIONS 50
6810 char balllocations
[50][55] = {
6811 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6812 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6813 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6814 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6815 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6816 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6817 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6818 "your bra", "your hair", "your bed", "the couch", "the wall",
6819 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6820 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6821 #define NUMOFPREPS 15
6822 char ballpreps
[50][50] = {
6823 "Near", "Somewhere near", "In", "In", "In",
6824 "In", "Hiding in", "Under", "Next to", "Over",
6825 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6826 #define NUMOFNUMS 34
6827 char ballnums
[50][50] = {
6828 "A hundred", "A thousand", "A few", "42",
6829 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6830 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6831 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6833 #define NUMOFMULTS 8
6834 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6837 * 0: normal (Not used in x3)
6844 if (method
== 1) /* A Color */
6848 answer
= (rand() % 12); /* Make sure this is the # of entries */
6851 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6853 case 1: strcpy(tmp
, "Sort of a light %s color.");
6855 case 2: strcpy(tmp
, "Dark and dreary %s.");
6857 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6859 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6861 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6863 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6865 case 10: strcpy(tmp
, "Solid %s.");
6867 case 11: strcpy(tmp
, "Transparent %s.");
6869 default: strcpy(outcome
, "An invalid random number was generated.");
6872 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6875 else if (method
== 2) /* Location */
6877 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6879 else if (method
== 3) /* Number of ___ */
6881 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6885 //Debug(DBGWARNING, "Error in 8ball.");
6890 static CHANSERV_FUNC(cmd_8ball
)
6892 char *word1
, *word2
, *word3
;
6893 static char eb
[MAXLEN
];
6894 unsigned int accum
, i
, j
;
6898 for(i
=1; i
<argc
; i
++)
6899 for(j
=0; argv
[i
][j
]; j
++)
6900 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6902 accum
+= time(NULL
)/3600;
6904 word2
= argc
>2?argv
[2]:"";
6905 word3
= argc
>3?argv
[3]:"";
6908 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6909 eightball(eb
, 1, accum
);
6910 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6911 eightball(eb
, 1, accum
);
6912 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6913 eightball(eb
, 1, accum
);
6914 /*** LOCATION *****/
6919 (strcasecmp(word1
, "where") == 0) &&
6920 (strcasecmp(word2
, "is") == 0)
6924 strcasecmp(word1
, "where's") == 0
6927 eightball(eb
, 2, accum
);
6929 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6930 eightball(eb
, 3, accum
);
6934 /* Generic 8ball question.. so pull from x3.conf srvx style */
6937 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6940 char response
[MAXLEN
];
6941 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6942 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6945 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6951 char response
[MAXLEN
];
6952 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6953 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6956 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6961 static CHANSERV_FUNC(cmd_d
)
6963 unsigned long sides
, count
, modifier
, ii
, total
;
6964 char response
[MAXLEN
], *sep
;
6968 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6978 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6979 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6983 else if((sep
[0] == '-') && isdigit(sep
[1]))
6984 modifier
= strtoul(sep
, NULL
, 10);
6985 else if((sep
[0] == '+') && isdigit(sep
[1]))
6986 modifier
= strtoul(sep
+1, NULL
, 10);
6993 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6998 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
7001 for(total
= ii
= 0; ii
< count
; ++ii
)
7002 total
+= (rand() % sides
) + 1;
7005 if((count
> 1) || modifier
)
7007 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
7008 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
7012 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
7013 sprintf(response
, fmt
, total
, sides
);
7016 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7018 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7022 static CHANSERV_FUNC(cmd_huggle
)
7024 /* CTCP must be via PRIVMSG, never notice */
7026 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
7028 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
7032 static CHANSERV_FUNC(cmd_calc
)
7034 char response
[MAXLEN
];
7037 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7040 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
7042 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
7046 static CHANSERV_FUNC(cmd_reply
)
7050 unsplit_string(argv
+ 1, argc
- 1, NULL
);
7053 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
7055 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
7060 chanserv_adjust_limit(void *data
)
7062 struct mod_chanmode change
;
7063 struct chanData
*cData
= data
;
7064 struct chanNode
*channel
= cData
->channel
;
7067 if(IsSuspended(cData
))
7070 cData
->limitAdjusted
= now
;
7071 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7072 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7074 if(limit
> cData
->modes
.new_limit
)
7075 limit
= cData
->modes
.new_limit
;
7076 else if(limit
== cData
->modes
.new_limit
)
7080 mod_chanmode_init(&change
);
7081 change
.modes_set
= MODE_LIMIT
;
7082 change
.new_limit
= limit
;
7083 mod_chanmode_announce(chanserv
, channel
, &change
);
7087 handle_new_channel(struct chanNode
*channel
)
7089 struct chanData
*cData
;
7091 if(!(cData
= channel
->channel_info
))
7094 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7095 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7097 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7098 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7101 /* Welcome to my worst nightmare. Warning: Read (or modify)
7102 the code below at your own risk. */
7104 handle_join(struct modeNode
*mNode
)
7106 struct mod_chanmode change
;
7107 struct userNode
*user
= mNode
->user
;
7108 struct chanNode
*channel
= mNode
->channel
;
7109 struct chanData
*cData
;
7110 struct userData
*uData
= NULL
;
7111 struct banData
*bData
;
7112 struct handle_info
*handle
;
7113 unsigned int modes
= 0, info
= 0;
7116 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7119 cData
= channel
->channel_info
;
7120 if(channel
->members
.used
> cData
->max
)
7121 cData
->max
= channel
->members
.used
;
7124 /* Check for bans. If they're joining through a ban, one of two
7126 * 1: Join during a netburst, by riding the break. Kick them
7127 * unless they have ops or voice in the channel.
7128 * 2: They're allowed to join through the ban (an invite in
7129 * ircu2.10, or a +e on Hybrid, or something).
7130 * If they're not joining through a ban, and the banlist is not
7131 * full, see if they're on the banlist for the channel. If so,
7134 if(user
->uplink
->burst
&& !mNode
->modes
)
7137 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7139 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7141 /* Riding a netburst. Naughty. */
7142 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7149 if(user
->handle_info
)
7151 handle
= user
->handle_info
;
7154 uData
= GetTrueChannelAccess(cData
, handle
);
7160 mod_chanmode_init(&change
);
7163 /* TODO: maybe only people above inviteme level? -Rubin */
7164 /* We don't kick people with access */
7167 if(channel
->banlist
.used
< MAXBANS
)
7169 /* Not joining through a ban. */
7170 for(bData
= cData
->bans
;
7171 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7172 bData
= bData
->next
);
7176 char kick_reason
[MAXLEN
];
7177 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7179 bData
->triggered
= now
;
7180 if(bData
!= cData
->bans
)
7182 /* Shuffle the ban to the head of the list. */
7184 bData
->next
->prev
= bData
->prev
;
7186 bData
->prev
->next
= bData
->next
;
7189 bData
->next
= cData
->bans
;
7192 cData
->bans
->prev
= bData
;
7193 cData
->bans
= bData
;
7196 change
.args
[0].mode
= MODE_BAN
;
7197 change
.args
[0].u
.hostmask
= bData
->mask
;
7198 mod_chanmode_announce(chanserv
, channel
, &change
);
7199 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7205 /* ChanServ will not modify the limits in join-flooded channels.
7206 It will also skip DynLimit processing when the user (or srvx)
7207 is bursting in, because there are likely more incoming. */
7208 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7209 && !user
->uplink
->burst
7210 && !channel
->join_flooded
7211 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7213 /* The user count has begun "bumping" into the channel limit,
7214 so set a timer to raise the limit a bit. Any previous
7215 timers are removed so three incoming users within the delay
7216 results in one limit change, not three. */
7218 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7219 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7222 /* Give automodes exept during join-floods */
7223 if(!channel
->join_flooded
)
7225 if(cData
->chOpts
[chAutomode
] == 'v')
7226 modes
|= MODE_VOICE
;
7227 else if(cData
->chOpts
[chAutomode
] == 'h')
7228 modes
|= MODE_HALFOP
;
7229 else if(cData
->chOpts
[chAutomode
] == 'o')
7230 modes
|= MODE_CHANOP
;
7233 greeting
= cData
->greeting
;
7234 if(user
->handle_info
)
7236 /* handle = user->handle_info; */
7238 if(IsHelper(user
) && !IsHelping(user
))
7241 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7243 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7245 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7251 /* uData = GetTrueChannelAccess(cData, handle); */
7252 if(uData
&& !IsUserSuspended(uData
))
7254 /* non users getting automodes are handled above. */
7255 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7257 /* just op everyone with access */
7258 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7259 modes
|= MODE_VOICE
;
7260 /* or do their access level */
7261 else if(uData
->access
>= UL_OP
)
7262 modes
|= MODE_CHANOP
;
7263 else if(uData
->access
>= UL_HALFOP
)
7264 modes
|= MODE_HALFOP
;
7265 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7266 modes
|= MODE_VOICE
;
7268 if(uData
->access
>= UL_PRESENT
)
7269 cData
->visited
= now
;
7270 if(cData
->user_greeting
)
7271 greeting
= cData
->user_greeting
;
7273 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7274 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7282 /* If user joining normally (not during burst), apply op or voice,
7283 * and send greeting/userinfo as appropriate.
7285 if(!user
->uplink
->burst
)
7289 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7290 if(modes & MODE_CHANOP) {
7291 modes &= ~MODE_HALFOP;
7292 modes &= ~MODE_VOICE;
7295 change
.args
[0].mode
= modes
;
7296 change
.args
[0].u
.member
= mNode
;
7297 mod_chanmode_announce(chanserv
, channel
, &change
);
7300 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7302 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7308 chanserv_autojoin_channels(struct userNode
*user
)
7310 struct userData
*channel
;
7312 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7314 struct chanNode
*cn
;
7315 struct modeNode
*mn
;
7317 if(IsUserSuspended(channel
)
7318 || IsSuspended(channel
->channel
)
7319 || !(cn
= channel
->channel
->channel
))
7322 mn
= GetUserMode(cn
, user
);
7325 if(!IsUserSuspended(channel
)
7326 && IsUserAutoJoin(channel
)
7327 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7329 && !user
->uplink
->burst
)
7330 irc_svsjoin(chanserv
, user
, cn
);
7336 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7338 struct mod_chanmode change
;
7339 struct userData
*channel
;
7340 unsigned int ii
, jj
, i
;
7342 if(!user
->handle_info
)
7345 mod_chanmode_init(&change
);
7347 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7349 struct chanNode
*cn
;
7350 struct chanData
*cData
;
7351 struct modeNode
*mn
;
7352 if(IsUserSuspended(channel
)
7353 || IsSuspended(channel
->channel
)
7354 || !(cn
= channel
->channel
->channel
))
7357 cData
= cn
->channel_info
;
7358 mn
= GetUserMode(cn
, user
);
7361 if(!IsUserSuspended(channel
)
7362 && IsUserAutoInvite(channel
)
7363 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7365 && !user
->uplink
->burst
)
7366 irc_invite(chanserv
, user
, cn
);
7370 if(channel
->access
>= UL_PRESENT
)
7371 channel
->channel
->visited
= now
;
7373 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
7375 if(channel
->access
>= UL_OP
)
7376 change
.args
[0].mode
= MODE_CHANOP
;
7377 else if(channel
->access
>= UL_HALFOP
)
7378 change
.args
[0].mode
= MODE_HALFOP
;
7379 else if(channel
->access
>= UL_PEON
)
7380 change
.args
[0].mode
= MODE_VOICE
;
7382 change
.args
[0].mode
= 0;
7383 change
.args
[0].u
.member
= mn
;
7384 if(change
.args
[0].mode
)
7385 mod_chanmode_announce(chanserv
, cn
, &change
);
7388 channel
->seen
= now
;
7389 channel
->present
= 1;
7392 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7394 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7395 struct banData
*ban
;
7397 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7398 || !channel
->channel_info
7399 || IsSuspended(channel
->channel_info
))
7401 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7403 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7404 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7406 if(jj
< channel
->banlist
.used
)
7408 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7410 char kick_reason
[MAXLEN
];
7411 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7413 change
.args
[0].mode
= MODE_BAN
;
7414 change
.args
[0].u
.hostmask
= ban
->mask
;
7415 mod_chanmode_announce(chanserv
, channel
, &change
);
7416 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7417 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7418 ban
->triggered
= now
;
7423 if(IsSupportHelper(user
))
7425 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7427 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7429 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7435 if (user
->handle_info
->ignores
->used
) {
7436 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7437 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7441 if (user
->handle_info
->epithet
)
7442 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7444 /* process autojoin channels 5 seconds later as this sometimes
7445 happens before autohide */
7446 // timeq_add(now + 5, chanserv_autojoin_channels, user);
7447 chanserv_autojoin_channels(user
);
7451 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7453 struct chanData
*cData
;
7454 struct userData
*uData
;
7456 cData
= mn
->channel
->channel_info
;
7457 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7460 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7462 /* Allow for a bit of padding so that the limit doesn't
7463 track the user count exactly, which could get annoying. */
7464 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7466 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7467 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7471 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7473 scan_user_presence(uData
, mn
->user
);
7477 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7479 unsigned int ii
, jj
;
7480 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7482 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7483 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7485 if(jj
< mn
->user
->channels
.used
)
7488 if(ii
== chanserv_conf
.support_channels
.used
)
7489 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7494 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7496 struct userData
*uData
;
7498 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7499 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7500 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7503 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
7505 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7506 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7509 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7514 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7516 struct chanData
*cData
;
7518 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7521 cData
= channel
->channel_info
;
7522 if(bad_topic(channel
, user
, channel
->topic
))
7523 { /* User doesnt have privs to set topics. Undo it */
7524 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7525 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7528 /* If there is a topic mask set, and the new topic doesnt match,
7529 * set the topic to mask + new_topic */
7530 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7532 char new_topic
[TOPICLEN
+1];
7533 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7536 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7537 /* and fall through to topicsnarf code below.. */
7539 else /* Topic couldnt fit into mask, was too long */
7541 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7542 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7543 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7547 /* With topicsnarf, grab the topic and save it as the default topic. */
7548 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7551 cData
->topic
= strdup(channel
->topic
);
7557 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7559 struct mod_chanmode
*bounce
= NULL
;
7560 unsigned int bnc
, ii
;
7563 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7566 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7567 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7569 char correct
[MAXLEN
];
7570 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7571 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7572 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7574 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7576 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7578 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7579 if(!protect_user(victim
, user
, channel
->channel_info
, false))
7582 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7585 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7586 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7587 if(bounce
->args
[bnc
].u
.member
)
7591 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7592 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7594 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7596 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7598 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7599 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7602 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7603 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7604 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7607 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7609 const char *ban
= change
->args
[ii
].u
.hostmask
;
7610 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7613 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7614 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7615 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7617 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7622 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7623 mod_chanmode_announce(chanserv
, channel
, bounce
);
7624 for(ii
= 0; ii
< change
->argc
; ++ii
)
7625 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7626 free((char*)bounce
->args
[ii
].u
.hostmask
);
7627 mod_chanmode_free(bounce
);
7632 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7634 struct chanNode
*channel
;
7635 struct banData
*bData
;
7636 struct mod_chanmode change
;
7637 unsigned int ii
, jj
;
7638 char kick_reason
[MAXLEN
];
7640 mod_chanmode_init(&change
);
7642 change
.args
[0].mode
= MODE_BAN
;
7643 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7645 channel
= user
->channels
.list
[ii
]->channel
;
7646 /* Need not check for bans if they're opped or voiced. */
7647 /* TODO: does this make sense in automode v, h, and o? *
7648 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7649 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7651 /* Need not check for bans unless channel registration is active. */
7652 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7654 /* Look for a matching ban already on the channel. */
7655 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7656 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7658 /* Need not act if we found one. */
7659 if(jj
< channel
->banlist
.used
)
7661 /* don't kick someone on the userlist */
7662 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7664 /* Look for a matching ban in this channel. */
7665 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7667 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7669 change
.args
[0].u
.hostmask
= bData
->mask
;
7670 mod_chanmode_announce(chanserv
, channel
, &change
);
7671 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7672 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7673 bData
->triggered
= now
;
7674 break; /* we don't need to check any more bans in the channel */
7679 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7681 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7685 dict_remove2(handle_dnrs
, old_handle
, 1);
7686 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7687 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7692 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7694 struct userNode
*h_user
;
7696 if(handle
->channels
)
7698 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7699 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7701 while(handle
->channels
)
7702 del_channel_user(handle
->channels
, 1);
7707 handle_server_link(UNUSED_ARG(struct server
*server
))
7709 struct chanData
*cData
;
7711 for(cData
= channelList
; cData
; cData
= cData
->next
)
7713 if(!IsSuspended(cData
))
7714 cData
->may_opchan
= 1;
7715 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7716 && !cData
->channel
->join_flooded
7717 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7718 < chanserv_conf
.adjust_threshold
))
7720 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7721 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7727 chanserv_conf_read(void)
7731 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7732 struct mod_chanmode
*change
;
7733 struct string_list
*strlist
;
7734 struct chanNode
*chan
;
7737 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7739 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7742 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7743 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7744 chanserv_conf
.support_channels
.used
= 0;
7745 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7747 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7749 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7752 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7754 channelList_append(&chanserv_conf
.support_channels
, chan
);
7757 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7760 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7763 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7765 channelList_append(&chanserv_conf
.support_channels
, chan
);
7767 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7768 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7769 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7770 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7771 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7772 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7773 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7774 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7775 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7776 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7777 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7778 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7779 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7780 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7781 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7782 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7783 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7784 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7785 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7786 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7787 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7788 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7789 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7790 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7791 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7793 NickChange(chanserv
, str
, 0);
7794 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7795 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7796 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7797 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7798 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7799 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7800 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7801 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7802 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7803 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7804 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7805 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7806 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7807 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7808 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7809 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7810 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7811 god_timeout
= str
? ParseInterval(str
) : 60*15;
7812 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7815 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7816 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7817 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7818 && (change
->argc
< 2))
7820 chanserv_conf
.default_modes
= *change
;
7821 mod_chanmode_free(change
);
7823 free_string_list(chanserv_conf
.set_shows
);
7824 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7826 strlist
= string_list_copy(strlist
);
7829 static const char *list
[] = {
7830 /* free form text */
7831 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7832 /* options based on user level */
7833 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7834 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7835 /* multiple choice options */
7836 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7837 /* binary options */
7838 "DynLimit", "NoDelete", "BanTimeout",
7843 strlist
= alloc_string_list(ArrayLength(list
)-1);
7844 for(ii
=0; list
[ii
]; ii
++)
7845 string_list_append(strlist
, strdup(list
[ii
]));
7847 chanserv_conf
.set_shows
= strlist
;
7848 /* We don't look things up now, in case the list refers to options
7849 * defined by modules initialized after this point. Just mark the
7850 * function list as invalid, so it will be initialized.
7852 set_shows_list
.used
= 0;
7853 free_string_list(chanserv_conf
.eightball
);
7854 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7857 strlist
= string_list_copy(strlist
);
7861 strlist
= alloc_string_list(4);
7862 string_list_append(strlist
, strdup("Yes."));
7863 string_list_append(strlist
, strdup("No."));
7864 string_list_append(strlist
, strdup("Maybe so."));
7866 chanserv_conf
.eightball
= strlist
;
7867 free_string_list(chanserv_conf
.old_ban_names
);
7868 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7870 strlist
= string_list_copy(strlist
);
7872 strlist
= alloc_string_list(2);
7873 chanserv_conf
.old_ban_names
= strlist
;
7874 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7875 off_channel
= str
? atoi(str
) : 0;
7879 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7882 struct note_type
*ntype
;
7885 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7887 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7890 if(!(ntype
= chanserv_create_note_type(key
)))
7892 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7896 /* Figure out set access */
7897 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7899 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7900 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7902 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7904 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7905 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7907 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7909 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7913 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7914 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7915 ntype
->set_access
.min_opserv
= 0;
7918 /* Figure out visibility */
7919 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7920 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7921 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7922 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7923 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7924 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7925 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7926 ntype
->visible_type
= NOTE_VIS_ALL
;
7928 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7930 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7931 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7935 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7937 struct handle_info
*handle
;
7938 struct userData
*uData
;
7939 char *seen
, *inf
, *flags
, *expires
;
7941 unsigned short access
;
7943 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7945 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7949 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7950 if(access
> UL_OWNER
)
7952 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7956 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7957 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7958 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7959 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7960 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7961 handle
= get_handle_info(key
);
7964 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7968 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7969 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7970 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7972 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7974 if(uData
->expires
> now
)
7975 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7977 uData
->flags
&= ~USER_SUSPENDED
;
7980 /* Upgrade: set autoop to the inverse of noautoop */
7981 if(chanserv_read_version
< 2)
7983 /* if noautoop is true, set autoop false, and vice versa */
7984 if(uData
->flags
& USER_NOAUTO_OP
)
7985 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7987 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7988 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
);
7994 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7996 struct banData
*bData
;
7997 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7998 time_t set_time
, triggered_time
, expires_time
;
8000 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
8002 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
8006 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
8007 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
8008 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
8009 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
8010 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
8011 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
8012 if (!reason
|| !owner
)
8015 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
8016 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
8018 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
8020 expires_time
= set_time
+ atoi(s_duration
);
8024 if(!reason
|| (expires_time
&& (expires_time
< now
)))
8027 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
8030 static struct suspended
*
8031 chanserv_read_suspended(dict_t obj
)
8033 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
8037 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
8038 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8039 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
8040 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8041 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8042 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8043 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
8044 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
8045 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8046 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
8050 static struct giveownership
*
8051 chanserv_read_giveownership(dict_t obj
)
8053 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
8057 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
8058 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
8060 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
8062 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
8063 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8065 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8066 giveownership
->reason
= str
? strdup(str
) : NULL
;
8067 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8068 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8070 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8071 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8072 return giveownership
;
8076 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8078 struct suspended
*suspended
;
8079 struct giveownership
*giveownership
;
8080 struct mod_chanmode
*modes
;
8081 struct chanNode
*cNode
;
8082 struct chanData
*cData
;
8083 struct dict
*channel
, *obj
;
8084 char *str
, *argv
[10];
8088 channel
= hir
->d
.object
;
8090 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8093 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8096 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8099 cData
= register_channel(cNode
, str
);
8102 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8106 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8108 enum levelOption lvlOpt
;
8109 enum charOption chOpt
;
8111 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8112 cData
->flags
= atoi(str
);
8114 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8116 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8118 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8119 else if(levelOptions
[lvlOpt
].old_flag
)
8121 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8122 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8124 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8128 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8130 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8132 cData
->chOpts
[chOpt
] = str
[0];
8135 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8137 enum levelOption lvlOpt
;
8138 enum charOption chOpt
;
8141 cData
->flags
= base64toint(str
, 5);
8142 count
= strlen(str
+= 5);
8143 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8146 if(levelOptions
[lvlOpt
].old_flag
)
8148 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8149 lvl
= levelOptions
[lvlOpt
].flag_value
;
8151 lvl
= levelOptions
[lvlOpt
].default_value
;
8153 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8155 case 'c': lvl
= UL_COOWNER
; break;
8156 case 'm': lvl
= UL_MANAGER
; break;
8157 case 'n': lvl
= UL_OWNER
+1; break;
8158 case 'o': lvl
= UL_OP
; break;
8159 case 'p': lvl
= UL_PEON
; break;
8160 case 'h': lvl
= UL_HALFOP
; break;
8161 case 'w': lvl
= UL_OWNER
; break;
8162 default: lvl
= 0; break;
8164 cData
->lvlOpts
[lvlOpt
] = lvl
;
8166 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8167 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8170 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8172 suspended
= chanserv_read_suspended(obj
);
8173 cData
->suspended
= suspended
;
8174 suspended
->cData
= cData
;
8175 /* We could use suspended->expires and suspended->revoked to
8176 * set the CHANNEL_SUSPENDED flag, but we don't. */
8178 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8180 suspended
= calloc(1, sizeof(*suspended
));
8181 suspended
->issued
= 0;
8182 suspended
->revoked
= 0;
8183 suspended
->suspender
= strdup(str
);
8184 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8185 suspended
->expires
= str
? atoi(str
) : 0;
8186 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8187 suspended
->reason
= strdup(str
? str
: "No reason");
8188 suspended
->previous
= NULL
;
8189 cData
->suspended
= suspended
;
8190 suspended
->cData
= cData
;
8194 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8195 suspended
= NULL
; /* to squelch a warning */
8198 if(IsSuspended(cData
)) {
8199 if(suspended
->expires
> now
)
8200 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8201 else if(suspended
->expires
)
8202 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8205 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8207 giveownership
= chanserv_read_giveownership(obj
);
8208 cData
->giveownership
= giveownership
;
8211 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8212 struct mod_chanmode change
;
8213 mod_chanmode_init(&change
);
8215 change
.args
[0].mode
= MODE_CHANOP
;
8216 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8217 mod_chanmode_announce(chanserv
, cNode
, &change
);
8220 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8221 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8222 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8223 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8224 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8225 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8226 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8227 cData
->max
= str
? atoi(str
) : 0;
8228 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8229 cData
->greeting
= str
? strdup(str
) : NULL
;
8230 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8231 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8232 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8233 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8234 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8235 cData
->topic
= str
? strdup(str
) : NULL
;
8237 if(!IsSuspended(cData
)
8238 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8239 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8240 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8241 cData
->modes
= *modes
;
8243 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8244 if(cData
->modes
.argc
> 1)
8245 cData
->modes
.argc
= 1;
8246 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8247 mod_chanmode_free(modes
);
8250 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8251 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8252 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8254 if(!cData
->users
&& !IsProtected(cData
))
8256 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8257 unregister_channel(cData
, "has empty user list.");
8261 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8262 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8263 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8265 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8266 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8268 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8269 struct record_data
*rd
= iter_data(it
);
8270 const char *note
, *setter
;
8272 if(rd
->type
!= RECDB_OBJECT
)
8274 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8278 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8280 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8282 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8286 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8287 if(!setter
) setter
= "<unknown>";
8288 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8296 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8298 const char *setter
, *reason
, *str
;
8299 struct do_not_register
*dnr
;
8301 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8304 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8307 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8310 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8313 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8316 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8318 dnr
->set
= atoi(str
);
8324 chanserv_version_read(struct dict
*section
)
8328 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8330 chanserv_read_version
= atoi(str
);
8331 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8335 chanserv_saxdb_read(struct dict
*database
)
8337 struct dict
*section
;
8340 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8341 chanserv_version_read(section
);
8343 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8344 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8345 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8347 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8348 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8349 chanserv_channel_read(iter_key(it
), iter_data(it
));
8351 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8352 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8353 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8359 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8361 int high_present
= 0;
8362 saxdb_start_record(ctx
, KEY_USERS
, 1);
8363 for(; uData
; uData
= uData
->next
)
8365 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8367 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8368 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8369 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8371 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8373 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8375 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8376 saxdb_end_record(ctx
);
8378 saxdb_end_record(ctx
);
8379 return high_present
;
8383 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8387 saxdb_start_record(ctx
, KEY_BANS
, 1);
8388 for(; bData
; bData
= bData
->next
)
8390 saxdb_start_record(ctx
, bData
->mask
, 0);
8391 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8392 if(bData
->triggered
)
8393 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8395 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8397 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8399 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8400 saxdb_end_record(ctx
);
8402 saxdb_end_record(ctx
);
8406 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8408 saxdb_start_record(ctx
, name
, 0);
8409 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8410 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8412 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8414 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8416 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8418 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8419 saxdb_end_record(ctx
);
8423 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8425 saxdb_start_record(ctx
, name
, 0);
8426 if(giveownership
->staff_issuer
)
8427 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8428 if(giveownership
->old_owner
)
8429 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8430 if(giveownership
->target
)
8431 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8432 if(giveownership
->target_access
)
8433 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8434 if(giveownership
->reason
)
8435 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8436 if(giveownership
->issued
)
8437 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8438 if(giveownership
->previous
)
8439 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8440 saxdb_end_record(ctx
);
8444 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8448 enum levelOption lvlOpt
;
8449 enum charOption chOpt
;
8451 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8453 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8454 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8456 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8457 if(channel
->registrar
)
8458 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8459 if(channel
->greeting
)
8460 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8461 if(channel
->user_greeting
)
8462 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8463 if(channel
->topic_mask
)
8464 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8465 if(channel
->suspended
)
8466 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8467 if(channel
->giveownership
)
8468 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8470 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8471 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8472 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8473 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8474 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8476 buf
[0] = channel
->chOpts
[chOpt
];
8478 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8480 saxdb_end_record(ctx
);
8482 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8484 mod_chanmode_format(&channel
->modes
, buf
);
8485 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8488 high_present
= chanserv_write_users(ctx
, channel
->users
);
8489 chanserv_write_bans(ctx
, channel
->bans
);
8491 if(dict_size(channel
->notes
))
8495 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8496 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8498 struct note
*note
= iter_data(it
);
8499 saxdb_start_record(ctx
, iter_key(it
), 0);
8500 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8501 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8502 saxdb_end_record(ctx
);
8504 saxdb_end_record(ctx
);
8507 if(channel
->ownerTransfer
)
8508 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8509 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8510 saxdb_end_record(ctx
);
8514 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8518 saxdb_start_record(ctx
, ntype
->name
, 0);
8519 switch(ntype
->set_access_type
)
8521 case NOTE_SET_CHANNEL_ACCESS
:
8522 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8524 case NOTE_SET_CHANNEL_SETTER
:
8525 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8527 case NOTE_SET_PRIVILEGED
: default:
8528 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8531 switch(ntype
->visible_type
)
8533 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8534 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8535 case NOTE_VIS_PRIVILEGED
:
8536 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8538 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8539 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8540 saxdb_end_record(ctx
);
8544 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8546 struct do_not_register
*dnr
;
8549 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8551 dnr
= iter_data(it
);
8552 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8554 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8555 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8556 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8557 saxdb_end_record(ctx
);
8562 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8565 struct chanData
*channel
;
8567 /* Version Control*/
8568 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8569 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8570 saxdb_end_record(ctx
);
8573 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8574 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8575 chanserv_write_note_type(ctx
, iter_data(it
));
8576 saxdb_end_record(ctx
);
8579 saxdb_start_record(ctx
, KEY_DNR
, 1);
8580 write_dnrs_helper(ctx
, handle_dnrs
);
8581 write_dnrs_helper(ctx
, plain_dnrs
);
8582 write_dnrs_helper(ctx
, mask_dnrs
);
8583 saxdb_end_record(ctx
);
8586 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8587 for(channel
= channelList
; channel
; channel
= channel
->next
)
8588 chanserv_write_channel(ctx
, channel
);
8589 saxdb_end_record(ctx
);
8595 chanserv_db_cleanup(void) {
8597 unreg_part_func(handle_part
);
8599 unregister_channel(channelList
, "terminating.");
8600 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8601 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8602 free(chanserv_conf
.support_channels
.list
);
8603 dict_delete(handle_dnrs
);
8604 dict_delete(plain_dnrs
);
8605 dict_delete(mask_dnrs
);
8606 dict_delete(note_types
);
8607 free_string_list(chanserv_conf
.eightball
);
8608 free_string_list(chanserv_conf
.old_ban_names
);
8609 free_string_list(chanserv_conf
.set_shows
);
8610 free(set_shows_list
.list
);
8611 free(uset_shows_list
.list
);
8614 struct userData
*helper
= helperList
;
8615 helperList
= helperList
->next
;
8620 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8621 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8622 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8625 init_chanserv(const char *nick
)
8627 struct chanNode
*chan
;
8629 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8630 conf_register_reload(chanserv_conf_read
);
8632 reg_server_link_func(handle_server_link
);
8634 reg_new_channel_func(handle_new_channel
);
8635 reg_join_func(handle_join
);
8636 reg_part_func(handle_part
);
8637 reg_kick_func(handle_kick
);
8638 reg_topic_func(handle_topic
);
8639 reg_mode_change_func(handle_mode
);
8640 reg_nick_change_func(handle_nick_change
);
8642 reg_auth_func(handle_auth
);
8643 reg_handle_rename_func(handle_rename
);
8644 reg_unreg_func(handle_unreg
);
8646 handle_dnrs
= dict_new();
8647 dict_set_free_data(handle_dnrs
, free
);
8648 plain_dnrs
= dict_new();
8649 dict_set_free_data(plain_dnrs
, free
);
8650 mask_dnrs
= dict_new();
8651 dict_set_free_data(mask_dnrs
, free
);
8653 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8654 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8655 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8656 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8657 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8658 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8659 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8660 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8661 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8662 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8664 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8666 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8667 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8669 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8670 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8671 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8672 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8673 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8675 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8676 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8677 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8678 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8679 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8680 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8682 DEFINE_COMMAND(levels
, 1, 0, NULL
);
8684 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8685 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8686 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8687 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8689 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8690 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8691 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8692 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8693 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8694 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8695 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8696 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8697 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8698 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8700 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8701 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8702 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8703 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8704 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8705 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8706 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8707 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8708 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8709 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8710 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8711 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8712 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8713 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8715 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8716 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8717 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8718 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8720 /* if you change dellamer access, see also places
8721 * like unbanme which have manager hardcoded. */
8722 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8723 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8725 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8727 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8729 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8730 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8731 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8732 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8733 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8734 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8735 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8736 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8737 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8738 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8739 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8740 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8742 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8743 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8745 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8746 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8747 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8748 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8750 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8751 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8752 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8753 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8754 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8756 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8757 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8758 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8759 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8760 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8761 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8762 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8763 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8765 /* Channel options */
8766 DEFINE_CHANNEL_OPTION(defaulttopic
);
8767 DEFINE_CHANNEL_OPTION(topicmask
);
8768 DEFINE_CHANNEL_OPTION(greeting
);
8769 DEFINE_CHANNEL_OPTION(usergreeting
);
8770 DEFINE_CHANNEL_OPTION(modes
);
8771 DEFINE_CHANNEL_OPTION(enfops
);
8772 DEFINE_CHANNEL_OPTION(enfhalfops
);
8773 DEFINE_CHANNEL_OPTION(automode
);
8774 DEFINE_CHANNEL_OPTION(protect
);
8775 DEFINE_CHANNEL_OPTION(enfmodes
);
8776 DEFINE_CHANNEL_OPTION(enftopic
);
8777 DEFINE_CHANNEL_OPTION(pubcmd
);
8778 DEFINE_CHANNEL_OPTION(userinfo
);
8779 DEFINE_CHANNEL_OPTION(dynlimit
);
8780 DEFINE_CHANNEL_OPTION(topicsnarf
);
8781 DEFINE_CHANNEL_OPTION(nodelete
);
8782 DEFINE_CHANNEL_OPTION(toys
);
8783 DEFINE_CHANNEL_OPTION(setters
);
8784 DEFINE_CHANNEL_OPTION(topicrefresh
);
8785 DEFINE_CHANNEL_OPTION(resync
);
8786 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8787 DEFINE_CHANNEL_OPTION(bantimeout
);
8788 DEFINE_CHANNEL_OPTION(inviteme
);
8790 DEFINE_CHANNEL_OPTION(offchannel
);
8791 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8793 /* Alias set topic to set defaulttopic for compatibility. */
8794 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8797 DEFINE_USER_OPTION(autoinvite
);
8798 DEFINE_USER_OPTION(autojoin
);
8799 DEFINE_USER_OPTION(info
);
8800 DEFINE_USER_OPTION(autoop
);
8802 /* Alias uset autovoice to uset autoop. */
8803 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8805 note_types
= dict_new();
8806 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8809 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8810 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8811 service_register(chanserv
)->trigger
= '!';
8812 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8815 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8817 if(chanserv_conf
.channel_expire_frequency
)
8818 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8820 if(chanserv_conf
.ban_timeout_frequency
)
8821 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8823 if(chanserv_conf
.refresh_period
)
8825 time_t next_refresh
;
8826 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8827 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8828 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8831 if (autojoin_channels
&& chanserv
) {
8832 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8833 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8834 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8838 reg_exit_func(chanserv_db_cleanup
);
8839 message_register_table(msgtab
);