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_COOWNER
, argv
[1], cmd
);
2807 static CHANSERV_FUNC(cmd_mdelmanager
)
2809 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2812 static CHANSERV_FUNC(cmd_mdelop
)
2814 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2817 static CHANSERV_FUNC(cmd_mdelpeon
)
2819 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2822 static CHANSERV_FUNC(cmd_mdelhalfop
)
2824 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2830 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2832 struct banData
*bData
, *next
;
2833 char interval
[INTERVALLEN
];
2838 limit
= now
- duration
;
2839 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2843 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2846 del_channel_ban(bData
);
2850 intervalString(interval
, duration
, user
->handle_info
);
2851 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2856 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
)
2858 struct userData
*actor
, *uData
, *next
;
2859 char interval
[INTERVALLEN
];
2863 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2864 if(min_access
> max_access
)
2866 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2870 if((actor
->access
<= max_access
) && !IsHelping(user
))
2872 reply("CSMSG_NO_ACCESS");
2877 limit
= now
- duration
;
2878 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2882 if((uData
->seen
> limit
)
2884 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2887 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2888 || (!max_access
&& (uData
->access
< actor
->access
)))
2890 del_channel_user(uData
, 1);
2898 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2900 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2904 static CHANSERV_FUNC(cmd_trim
)
2906 unsigned long duration
;
2907 unsigned short min_level
, max_level
;
2912 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2913 duration
= ParseInterval(argv
[2]);
2916 reply("CSMSG_CANNOT_TRIM");
2920 if(!irccasecmp(argv
[1], "lamers"))
2922 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
2925 else if(!irccasecmp(argv
[1], "users"))
2927 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
2930 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2932 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
2935 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2937 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
2942 reply("CSMSG_INVALID_TRIM", argv
[1]);
2947 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2948 to the user. cmd_all takes advantage of this. */
2949 static CHANSERV_FUNC(cmd_up
)
2951 struct mod_chanmode change
;
2952 struct userData
*uData
;
2955 mod_chanmode_init(&change
);
2957 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2958 if(!change
.args
[0].u
.member
)
2961 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2965 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2969 reply("CSMSG_GODMODE_UP", argv
[0]);
2972 else if(uData
->access
>= UL_OP
)
2974 change
.args
[0].mode
= MODE_CHANOP
;
2975 errmsg
= "CSMSG_ALREADY_OPPED";
2977 else if(uData
->access
>= UL_HALFOP
)
2979 change
.args
[0].mode
= MODE_HALFOP
;
2980 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2982 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2984 change
.args
[0].mode
= MODE_VOICE
;
2985 errmsg
= "CSMSG_ALREADY_VOICED";
2990 reply("CSMSG_NO_ACCESS");
2993 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2994 if(!change
.args
[0].mode
)
2997 reply(errmsg
, channel
->name
);
3000 modcmd_chanmode_announce(&change
);
3004 static CHANSERV_FUNC(cmd_down
)
3006 struct mod_chanmode change
;
3008 mod_chanmode_init(&change
);
3010 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
3011 if(!change
.args
[0].u
.member
)
3014 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3018 if(!change
.args
[0].u
.member
->modes
)
3021 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3025 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3026 modcmd_chanmode_announce(&change
);
3030 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
)
3032 struct userData
*cList
;
3034 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3036 if(IsSuspended(cList
->channel
)
3037 || IsUserSuspended(cList
)
3038 || !GetUserMode(cList
->channel
->channel
, user
))
3041 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3047 static CHANSERV_FUNC(cmd_upall
)
3049 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3052 static CHANSERV_FUNC(cmd_downall
)
3054 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3057 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3058 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3061 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
)
3063 unsigned int ii
, valid
;
3064 struct userNode
*victim
;
3065 struct mod_chanmode
*change
;
3067 change
= mod_chanmode_alloc(argc
- 1);
3069 for(ii
=valid
=0; ++ii
< argc
; )
3071 if(!(victim
= GetUserH(argv
[ii
])))
3073 change
->args
[valid
].mode
= mode
;
3074 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3075 if(!change
->args
[valid
].u
.member
)
3077 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3082 change
->argc
= valid
;
3083 if(valid
< (argc
-1))
3084 reply("CSMSG_PROCESS_FAILED");
3087 modcmd_chanmode_announce(change
);
3088 reply(action
, channel
->name
);
3090 mod_chanmode_free(change
);
3094 static CHANSERV_FUNC(cmd_op
)
3096 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3099 static CHANSERV_FUNC(cmd_hop
)
3101 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3104 static CHANSERV_FUNC(cmd_deop
)
3106 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3109 static CHANSERV_FUNC(cmd_dehop
)
3111 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3114 static CHANSERV_FUNC(cmd_voice
)
3116 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3119 static CHANSERV_FUNC(cmd_devoice
)
3121 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3125 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3131 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3133 struct modeNode
*mn
= channel
->members
.list
[ii
];
3135 if(IsService(mn
->user
))
3138 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3141 if(protect_user(mn
->user
, user
, channel
->channel_info
, false))
3145 victims
[(*victimCount
)++] = mn
;
3151 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3153 struct userNode
*victim
;
3154 struct modeNode
**victims
;
3155 unsigned int offset
, n
, victimCount
, duration
= 0;
3156 char *reason
= "Bye.", *ban
, *name
;
3157 char interval
[INTERVALLEN
];
3159 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3160 REQUIRE_PARAMS(offset
);
3163 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3164 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3166 /* Truncate the reason to a length of TOPICLEN, as
3167 the ircd does; however, leave room for an ellipsis
3168 and the kicker's nick. */
3169 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3173 if((victim
= GetUserH(argv
[1])))
3175 victims
= alloca(sizeof(victims
[0]));
3176 victims
[0] = GetUserMode(channel
, victim
);
3177 /* XXX: The comparison with ACTION_KICK is just because all
3178 * other actions can work on users outside the channel, and we
3179 * want to allow those (e.g. unbans) in that case. If we add
3180 * some other ejection action for in-channel users, change
3182 victimCount
= victims
[0] ? 1 : 0;
3184 if(IsService(victim
))
3187 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3191 if((action
== ACTION_KICK
) && !victimCount
)
3194 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3198 if(protect_user(victim
, user
, channel
->channel_info
, false))
3200 // This translates to send_message(user, cmd->parent->bot, ...)
3201 // if user is x3 (ctcp action) cmd is null and segfault.
3203 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3207 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3208 name
= victim
->nick
;
3212 if(!is_ircmask(argv
[1]))
3215 reply("MSG_NICK_UNKNOWN", argv
[1]);
3219 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3221 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3224 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3227 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3228 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3230 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3231 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3232 some creativity, but its not x3's job to be the ban censor anyway. */
3233 if(is_overmask(argv
[1]))
3236 reply("CSMSG_LAME_MASK", argv
[1]);
3240 if((action
== ACTION_KICK
) && (victimCount
== 0))
3243 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3247 name
= ban
= strdup(argv
[1]);
3250 /* Truncate the ban in place if necessary; we must ensure
3251 that 'ban' is a valid ban mask before sanitizing it. */
3252 sanitize_ircmask(ban
);
3254 if(action
& ACTION_ADD_LAMER
)
3256 struct banData
*bData
, *next
;
3258 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3261 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3266 if(action
& ACTION_ADD_TIMED_LAMER
)
3268 duration
= ParseInterval(argv
[2]);
3273 reply("CSMSG_DURATION_TOO_LOW");
3277 else if(duration
> (86400 * 365 * 2))
3280 reply("CSMSG_DURATION_TOO_HIGH");
3287 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3289 if(match_ircglobs(bData
->mask
, ban
))
3291 int exact
= !irccasecmp(bData
->mask
, ban
);
3293 /* The ban is redundant; there is already a ban
3294 with the same effect in place. */
3298 free(bData
->reason
);
3299 bData
->reason
= strdup(reason
);
3300 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3302 reply("CSMSG_REASON_CHANGE", ban
);
3306 if(exact
&& bData
->expires
)
3310 /* If the ban matches an existing one exactly,
3311 extend the expiration time if the provided
3312 duration is longer. */
3313 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3315 bData
->expires
= now
+ duration
;
3326 /* Delete the expiration timeq entry and
3327 requeue if necessary. */
3328 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3331 timeq_add(bData
->expires
, expire_ban
, bData
);
3335 /* automated kickban, dont reply */
3338 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3340 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3346 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3353 if(match_ircglobs(ban
, bData
->mask
))
3355 /* The ban we are adding makes previously existing
3356 bans redundant; silently remove them. */
3357 del_channel_ban(bData
);
3361 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
);
3363 name
= ban
= strdup(bData
->mask
);
3367 /* WHAT DOES THIS DO?? -Rubin */
3368 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3370 extern const char *hidden_host_suffix
;
3371 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3373 unsigned int l1
, l2
;
3376 l2
= strlen(old_name
);
3379 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3381 new_mask
= malloc(MAXLEN
);
3382 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3384 name
= ban
= new_mask
;
3389 if(action
& ACTION_BAN
)
3391 unsigned int exists
;
3392 struct mod_chanmode
*change
;
3394 if(channel
->banlist
.used
>= MAXBANS
)
3397 reply("CSMSG_BANLIST_FULL", channel
->name
);
3402 exists
= ChannelBanExists(channel
, ban
);
3403 change
= mod_chanmode_alloc(victimCount
+ 1);
3404 for(n
= 0; n
< victimCount
; ++n
)
3406 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3407 change
->args
[n
].u
.member
= victims
[n
];
3411 change
->args
[n
].mode
= MODE_BAN
;
3412 change
->args
[n
++].u
.hostmask
= ban
;
3416 modcmd_chanmode_announce(change
);
3418 mod_chanmode_announce(chanserv
, channel
, change
);
3419 mod_chanmode_free(change
);
3421 if(exists
&& (action
== ACTION_BAN
))
3424 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3430 if(action
& ACTION_ADD_LAMER
)
3432 char kick_reason
[MAXLEN
];
3433 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3435 for(n
= 0; n
< victimCount
; n
++) {
3436 if(!protect_user(victims
[n
]->user
, user
, channel
->channel_info
, true)) {
3437 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3441 else if(action
& ACTION_KICK
)
3443 char kick_reason
[MAXLEN
];
3444 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3446 for(n
= 0; n
< victimCount
; n
++) {
3447 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3453 /* No response, since it was automated. */
3455 else if(action
& ACTION_ADD_LAMER
)
3458 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3460 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3462 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3463 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3464 else if(action
& ACTION_BAN
)
3465 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3466 else if(action
& ACTION_KICK
&& victimCount
)
3467 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3473 static CHANSERV_FUNC(cmd_kickban
)
3475 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3478 static CHANSERV_FUNC(cmd_kick
)
3480 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3483 static CHANSERV_FUNC(cmd_ban
)
3485 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3488 static CHANSERV_FUNC(cmd_addlamer
)
3490 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3493 static CHANSERV_FUNC(cmd_addtimedlamer
)
3495 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3498 static struct mod_chanmode
*
3499 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3501 struct mod_chanmode
*change
;
3502 unsigned char *match
;
3503 unsigned int ii
, count
;
3505 match
= alloca(bans
->used
);
3508 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3510 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3511 MATCH_USENICK
| MATCH_VISIBLE
);
3518 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3520 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3527 change
= mod_chanmode_alloc(count
);
3528 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3532 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3533 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3535 assert(count
== change
->argc
);
3539 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3541 unsigned int jj
, ii
, count
;
3543 struct chanData
*channel
;
3545 struct mod_chanmode
*change
;
3547 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3548 /* Walk through every channel */
3549 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3550 switch(channel
->chOpts
[chBanTimeout
])
3552 default: case '0': continue; /* Dont remove bans in this chan */
3553 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3554 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3555 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3556 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3557 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3560 /* First find out how many bans were going to unset */
3561 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3562 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3566 /* At least one ban, so setup a removal */
3567 change
= mod_chanmode_alloc(count
);
3569 /* Walk over every ban in this channel.. */
3570 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3571 bn
= channel
->channel
->banlist
.list
[jj
];
3572 if (bn
->set
< bantimeout
) {
3573 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3575 /* Add this ban to the mode change */
3576 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3577 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3579 /* Pull this ban out of the list */
3580 banList_remove(&(channel
->channel
->banlist
), bn
);
3585 /* Send the modes to IRC */
3586 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3588 /* free memory from strdup above */
3589 for(ii
= 0; ii
< count
; ++ii
)
3590 free((char*)change
->args
[ii
].u
.hostmask
);
3592 mod_chanmode_free(change
);
3595 /* Set this function to run again */
3596 if(chanserv_conf
.ban_timeout_frequency
)
3597 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3602 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3604 struct userNode
*actee
;
3610 /* may want to allow a comma delimited list of users... */
3611 if(!(actee
= GetUserH(argv
[1])))
3613 if(!is_ircmask(argv
[1]))
3615 reply("MSG_NICK_UNKNOWN", argv
[1]);
3619 mask
= strdup(argv
[1]);
3622 /* We don't sanitize the mask here because ircu
3624 if(action
& ACTION_UNBAN
)
3626 struct mod_chanmode
*change
;
3627 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3632 modcmd_chanmode_announce(change
);
3633 for(ii
= 0; ii
< change
->argc
; ++ii
)
3634 free((char*)change
->args
[ii
].u
.hostmask
);
3635 mod_chanmode_free(change
);
3640 if(action
& ACTION_DEL_LAMER
)
3642 struct banData
*ban
, *next
;
3644 ban
= channel
->channel_info
->bans
; /* lamers */
3648 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3651 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3656 del_channel_ban(ban
);
3663 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3665 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3671 static CHANSERV_FUNC(cmd_unban
)
3673 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3676 static CHANSERV_FUNC(cmd_dellamer
)
3678 /* it doesn't necessarily have to remove the channel ban - may want
3679 to make that an option. */
3680 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3683 static CHANSERV_FUNC(cmd_unbanme
)
3685 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3686 long flags
= ACTION_UNBAN
;
3688 /* remove permanent bans if the user has the proper access. */
3689 if(uData
->access
>= UL_MANAGER
)
3690 flags
|= ACTION_DEL_LAMER
;
3692 argv
[1] = user
->nick
;
3693 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3696 static CHANSERV_FUNC(cmd_unbanall
)
3698 struct mod_chanmode
*change
;
3701 if(!channel
->banlist
.used
)
3703 reply("CSMSG_NO_BANS", channel
->name
);
3707 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3708 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3710 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3711 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3713 modcmd_chanmode_announce(change
);
3714 for(ii
= 0; ii
< change
->argc
; ++ii
)
3715 free((char*)change
->args
[ii
].u
.hostmask
);
3716 mod_chanmode_free(change
);
3717 reply("CSMSG_BANS_REMOVED", channel
->name
);
3721 static CHANSERV_FUNC(cmd_open
)
3723 struct mod_chanmode
*change
;
3726 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3728 change
= mod_chanmode_alloc(0);
3729 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3730 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3731 && channel
->channel_info
->modes
.modes_set
)
3732 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3733 modcmd_chanmode_announce(change
);
3734 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3735 for(ii
= 0; ii
< change
->argc
; ++ii
)
3736 free((char*)change
->args
[ii
].u
.hostmask
);
3737 mod_chanmode_free(change
);
3741 static CHANSERV_FUNC(cmd_myaccess
)
3743 static struct string_buffer sbuf
;
3744 struct handle_info
*target_handle
;
3745 struct userData
*uData
;
3748 target_handle
= user
->handle_info
;
3749 else if(!IsHelping(user
))
3751 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3754 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3757 if(!target_handle
->channels
)
3759 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3763 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3764 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3766 struct chanData
*cData
= uData
->channel
;
3768 if(uData
->access
> UL_OWNER
)
3770 if(IsProtected(cData
)
3771 && (target_handle
!= user
->handle_info
)
3772 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3775 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3776 if(uData
->flags
== USER_AUTO_OP
)
3777 string_buffer_append(&sbuf
, ',');
3778 if(IsUserSuspended(uData
))
3779 string_buffer_append(&sbuf
, 's');
3780 if(IsUserAutoOp(uData
))
3782 if(uData
->access
>= UL_OP
)
3783 string_buffer_append(&sbuf
, 'o');
3784 else if(uData
->access
>= UL_HALFOP
)
3785 string_buffer_append(&sbuf
, 'h');
3786 else if(uData
->access
>= UL_PEON
)
3787 string_buffer_append(&sbuf
, 'v');
3789 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3790 string_buffer_append(&sbuf
, 'i');
3791 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3792 string_buffer_append(&sbuf
, 'j');
3794 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3796 string_buffer_append_string(&sbuf
, ")]");
3797 string_buffer_append(&sbuf
, '\0');
3798 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3804 static CHANSERV_FUNC(cmd_access
)
3806 struct userNode
*target
;
3807 struct handle_info
*target_handle
;
3808 struct userData
*uData
;
3810 char prefix
[MAXLEN
];
3815 target_handle
= target
->handle_info
;
3817 else if((target
= GetUserH(argv
[1])))
3819 target_handle
= target
->handle_info
;
3821 else if(argv
[1][0] == '*')
3823 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3825 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3831 reply("MSG_NICK_UNKNOWN", argv
[1]);
3835 assert(target
|| target_handle
);
3837 if(target
== chanserv
)
3839 reply("CSMSG_IS_CHANSERV");
3847 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3852 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3855 reply("MSG_AUTHENTICATE");
3861 const char *epithet
= NULL
, *type
= NULL
;
3864 epithet
= chanserv_conf
.irc_operator_epithet
;
3867 else if(IsNetworkHelper(target
))
3869 epithet
= chanserv_conf
.network_helper_epithet
;
3870 type
= "network helper";
3872 else if(IsSupportHelper(target
))
3874 epithet
= chanserv_conf
.support_helper_epithet
;
3875 type
= "support helper";
3879 if(target_handle
->epithet
)
3880 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3882 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3884 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3888 sprintf(prefix
, "%s", target_handle
->handle
);
3891 if(!channel
->channel_info
)
3893 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3897 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3898 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3899 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3901 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3902 /* To prevent possible information leaks, only show infolines
3903 * if the requestor is in the channel or it's their own
3905 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3907 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3909 /* Likewise, only say it's suspended if the user has active
3910 * access in that channel or it's their own entry. */
3911 if(IsUserSuspended(uData
)
3912 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3913 || (user
->handle_info
== uData
->handle
)))
3915 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3920 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3926 /* This is never used...
3928 zoot_list(struct listData *list)
3930 struct userData *uData;
3931 unsigned int start, curr, highest, lowest;
3932 struct helpfile_table tmp_table;
3933 const char **temp, *msg;
3935 if(list->table.length == 1)
3938 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);
3940 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));
3941 msg = user_find_message(list->user, "MSG_NONE");
3942 send_message_type(4, list->user, list->bot, " %s", msg);
3944 tmp_table.width = list->table.width;
3945 tmp_table.flags = list->table.flags;
3946 list->table.contents[0][0] = " ";
3947 highest = list->highest;
3948 if(list->lowest != 0)
3949 lowest = list->lowest;
3950 else if(highest < 100)
3953 lowest = highest - 100;
3954 for(start = curr = 1; curr < list->table.length; )
3956 uData = list->users[curr-1];
3957 list->table.contents[curr++][0] = " ";
3958 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3961 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);
3963 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));
3964 temp = list->table.contents[--start];
3965 list->table.contents[start] = list->table.contents[0];
3966 tmp_table.contents = list->table.contents + start;
3967 tmp_table.length = curr - start;
3968 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3969 list->table.contents[start] = temp;
3971 highest = lowest - 1;
3972 lowest = (highest < 100) ? 0 : (highest - 99);
3979 normal_list(struct listData
*list
)
3983 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
);
3985 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
));
3986 if(list
->table
.length
== 1)
3988 msg
= user_find_message(list
->user
, "MSG_NONE");
3989 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3992 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3995 /* if these need changed, uncomment and customize
3997 clean_list(struct listData *list)
4001 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);
4003 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));
4004 if(list->table.length == 1)
4006 msg = user_find_message(list->user, "MSG_NONE");
4007 send_message_type(4, list->user, list->bot, " %s", msg);
4010 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4014 advanced_list(struct listData *list)
4018 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);
4020 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));
4021 if(list->table.length == 1)
4023 msg = user_find_message(list->user, "MSG_NONE");
4024 send_message_type(4, list->user, list->bot, " %s", msg);
4027 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4031 classic_list(struct listData *list)
4035 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4037 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4038 if(list->table.length == 1)
4040 msg = user_find_message(list->user, "MSG_NONE");
4041 send_message_type(4, list->user, list->bot, " %s", msg);
4044 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4049 userData_access_comp(const void *arg_a
, const void *arg_b
)
4051 const struct userData
*a
= *(struct userData
**)arg_a
;
4052 const struct userData
*b
= *(struct userData
**)arg_b
;
4054 if(a
->access
!= b
->access
)
4055 res
= b
->access
- a
->access
;
4057 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4062 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4064 void (*send_list
)(struct listData
*);
4065 struct userData
*uData
;
4066 struct listData lData
;
4067 unsigned int matches
;
4073 lData
.bot
= cmd
->parent
->bot
;
4074 lData
.channel
= channel
;
4075 lData
.lowest
= lowest
;
4076 lData
.highest
= highest
;
4077 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4078 send_list
= normal_list
;
4079 /* What does the following line do exactly?? */
4080 /*(void)zoot_list; ** since it doesn't show user levels */
4083 if(user->handle_info)
4085 switch(user->handle_info->userlist_style)
4087 case HI_STYLE_CLEAN:
4088 send_list = clean_list;
4090 case HI_STYLE_ADVANCED:
4091 send_list = advanced_list;
4093 case HI_STYLE_CLASSIC:
4094 send_list = classic_list;
4096 case HI_STYLE_NORMAL:
4098 send_list = normal_list;
4103 send_list
= normal_list
;
4105 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4107 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4109 if((uData
->access
< lowest
)
4110 || (uData
->access
> highest
)
4111 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4113 lData
.users
[matches
++] = uData
;
4115 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4117 lData
.table
.length
= matches
+1;
4118 lData
.table
.flags
= TABLE_NO_FREE
;
4119 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4121 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4122 lData
.table
.width
= 5; /* with level = 5 */
4124 lData
.table
.width
= 4; /* without = 4 */
4125 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4126 lData
.table
.contents
[0] = ary
;
4127 if(user
->handle_info
) {
4128 switch(user
->handle_info
->userlist_style
) {
4129 case HI_STYLE_CLASSIC
:
4132 case HI_STYLE_ADVANCED
:
4133 ary
[i
++] = "Access";
4136 case HI_STYLE_CLEAN
:
4137 ary
[i
++] = "Access";
4139 case HI_STYLE_NORMAL
:
4141 ary
[i
++] = "Access";
4146 ary
[i
++] = "Access";
4148 ary
[i
++] = "Account";
4149 ary
[i
] = "Last Seen";
4151 ary
[i
++] = "Status";
4152 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4154 struct userData
*uData
= lData
.users
[matches
-1];
4155 char seen
[INTERVALLEN
];
4158 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4159 lData
.table
.contents
[matches
] = ary
;
4160 if(user
->handle_info
) {
4161 switch(user
->handle_info
->userlist_style
) {
4162 case HI_STYLE_CLASSIC
:
4163 ary
[i
++] = strtab(uData
->access
);
4165 case HI_STYLE_ADVANCED
:
4166 ary
[i
++] = user_level_name_from_level(uData
->access
);
4167 ary
[i
++] = strtab(uData
->access
);
4169 case HI_STYLE_CLEAN
:
4170 ary
[i
++] = user_level_name_from_level(uData
->access
);
4172 case HI_STYLE_NORMAL
:
4174 ary
[i
++] = user_level_name_from_level(uData
->access
);
4179 ary
[i
++] = user_level_name_from_level(uData
->access
);
4181 ary
[i
++] = uData
->handle
->handle
;
4184 else if(!uData
->seen
)
4187 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4188 ary
[i
] = strdup(ary
[i
]);
4190 if(IsUserSuspended(uData
))
4191 ary
[i
++] = "Suspended";
4192 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4193 ary
[i
++] = "Vacation";
4195 ary
[i
++] = "Normal";
4198 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4200 /* Free strdup above */
4201 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4202 free(lData
.table
.contents
[matches
]);
4204 free(lData
.table
.contents
[0]);
4205 free(lData
.table
.contents
);
4209 /* Remove this now that debugging is over? or improve it for
4210 * users? Would it be better tied into USERS somehow? -Rubin */
4211 static CHANSERV_FUNC(cmd_pending
)
4213 struct adduserPending
*ap
;
4214 reply("CSMSG_ADDUSER_PENDING_HEADER");
4215 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4217 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4218 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4219 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4223 static CHANSERV_FUNC(cmd_users
)
4225 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4228 static CHANSERV_FUNC(cmd_wlist
)
4230 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4233 static CHANSERV_FUNC(cmd_clist
)
4235 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4238 static CHANSERV_FUNC(cmd_mlist
)
4240 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4243 static CHANSERV_FUNC(cmd_olist
)
4245 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4248 static CHANSERV_FUNC(cmd_hlist
)
4250 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4253 static CHANSERV_FUNC(cmd_plist
)
4255 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4258 static CHANSERV_FUNC(cmd_lamers
)
4260 struct helpfile_table tbl
;
4261 unsigned int matches
= 0, timed
= 0, ii
;
4262 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4263 const char *msg_never
, *triggered
, *expires
;
4264 struct banData
*ban
, **bans
; /* lamers */
4271 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4272 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4275 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4277 if(search
&& !match_ircglobs(search
, ban
->mask
))
4279 bans
[matches
++] = ban
;
4284 tbl
.length
= matches
+ 1;
4285 tbl
.width
= 4 + timed
;
4287 tbl
.flags
= TABLE_NO_FREE
;
4288 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4289 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4290 tbl
.contents
[0][0] = "Mask";
4291 tbl
.contents
[0][1] = "Set By";
4292 tbl
.contents
[0][2] = "Triggered";
4295 tbl
.contents
[0][3] = "Expires";
4296 tbl
.contents
[0][4] = "Reason";
4299 tbl
.contents
[0][3] = "Reason";
4302 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4303 /* reply("MSG_NONE"); */
4304 free(tbl
.contents
[0]);
4309 msg_never
= user_find_message(user
, "MSG_NEVER");
4310 for(ii
= 0; ii
< matches
; )
4316 else if(ban
->expires
)
4317 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4319 expires
= msg_never
;
4322 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4324 triggered
= msg_never
;
4326 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4327 tbl
.contents
[ii
][0] = ban
->mask
;
4328 tbl
.contents
[ii
][1] = ban
->owner
;
4329 tbl
.contents
[ii
][2] = strdup(triggered
);
4332 tbl
.contents
[ii
][3] = strdup(expires
);
4333 tbl
.contents
[ii
][4] = ban
->reason
;
4336 tbl
.contents
[ii
][3] = ban
->reason
;
4338 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4339 /* reply("MSG_MATCH_COUNT", matches); */
4340 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4342 free((char*)tbl
.contents
[ii
][2]);
4344 free((char*)tbl
.contents
[ii
][3]);
4345 free(tbl
.contents
[ii
]);
4347 free(tbl
.contents
[0]);
4354 * return + if the user does NOT have the right to set the topic, and
4355 * the topic is changed.
4358 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4360 struct chanData
*cData
= channel
->channel_info
;
4361 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4363 else if(cData
->topic
)
4364 return irccasecmp(new_topic
, cData
->topic
);
4371 * Makes a givin topic fit into a givin topic mask and returns
4374 * topic_mask - the mask to conform to
4375 * topic - the topic to make conform
4376 * new_topic - the pre-allocated char* to put the new topic into
4378 * modifies: new_topic
4381 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4383 //char *topic_mask = cData->topic_mask;
4385 int pos
=0, starpos
=-1, dpos
=0, len
;
4387 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4394 strcpy(new_topic
, "");
4397 len
= strlen(topic
);
4398 if((dpos
+ len
) > TOPICLEN
)
4399 len
= TOPICLEN
+ 1 - dpos
;
4400 memcpy(new_topic
+dpos
, topic
, len
);
4404 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4405 default: new_topic
[dpos
++] = tchar
; break;
4408 if((dpos
> TOPICLEN
) || tchar
)
4410 strcpy(new_topic
, "");
4413 new_topic
[dpos
] = 0;
4417 static CHANSERV_FUNC(cmd_topic
)
4419 struct chanData
*cData
;
4423 #ifdef WITH_PROTOCOL_P10
4427 cData
= channel
->channel_info
;
4432 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4433 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4434 reply("CSMSG_TOPIC_SET", cData
->topic
);
4438 reply("CSMSG_NO_TOPIC", channel
->name
);
4442 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4443 /* If they say "!topic *", use an empty topic. */
4444 if((topic
[0] == '*') && (topic
[1] == 0))
4447 if(bad_topic(channel
, user
, topic
))
4449 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4454 /* If there is a topicmask set, and the new topic doesnt match, make it */
4455 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4457 char *topic_mask
= cData
->topic_mask
;
4458 char new_topic
[TOPICLEN
+1];
4460 /* make a new topic fitting mask */
4461 conform_topic(topic_mask
, topic
, new_topic
);
4464 /* Topic couldnt fit into mask, was too long */
4465 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4466 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4469 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4471 else /* No mask set, just set the topic */
4472 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4475 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4477 /* Grab the topic and save it as the default topic. */
4479 cData
->topic
= strdup(channel
->topic
);
4485 static CHANSERV_FUNC(cmd_mode
)
4487 struct userData
*uData
;
4488 struct mod_chanmode
*change
;
4493 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4494 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4498 change
= &channel
->channel_info
->modes
;
4499 if(change
->modes_set
|| change
->modes_clear
) {
4500 modcmd_chanmode_announce(change
);
4501 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4503 reply("CSMSG_NO_MODES", channel
->name
);
4507 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4509 base_oplevel
= MAXOPLEVEL
;
4510 else if (uData
->access
>= UL_OWNER
)
4513 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4514 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4518 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4522 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4523 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4526 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4527 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4531 modcmd_chanmode_announce(change
);
4532 mod_chanmode_free(change
);
4533 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4537 static CHANSERV_FUNC(cmd_invite
)
4539 struct userData
*uData
;
4540 struct userNode
*invite
;
4542 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4546 if(!(invite
= GetUserH(argv
[1])))
4548 reply("MSG_NICK_UNKNOWN", argv
[1]);
4555 if(GetUserMode(channel
, invite
))
4557 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4565 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4566 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4569 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4572 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4574 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4575 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4576 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4582 irc_invite(chanserv
, invite
, channel
);
4584 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4589 static CHANSERV_FUNC(cmd_inviteme
)
4591 if(GetUserMode(channel
, user
))
4593 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4596 if(channel
->channel_info
4597 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4599 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4602 irc_invite(cmd
->parent
->bot
, user
, channel
);
4607 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4610 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4612 /* We display things based on two dimensions:
4613 * - Issue time: present or absent
4614 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4615 * (in order of precedence, so something both expired and revoked
4616 * only counts as revoked)
4618 combo
= (suspended
->issued
? 4 : 0)
4619 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4621 case 0: /* no issue time, indefinite expiration */
4622 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4624 case 1: /* no issue time, expires in future */
4625 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4626 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4628 case 2: /* no issue time, expired */
4629 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4630 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4632 case 3: /* no issue time, revoked */
4633 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4634 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4636 case 4: /* issue time set, indefinite expiration */
4637 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4638 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4640 case 5: /* issue time set, expires in future */
4641 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4642 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4643 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4645 case 6: /* issue time set, expired */
4646 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4647 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4648 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4650 case 7: /* issue time set, revoked */
4651 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4652 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4653 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4656 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4662 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4665 const char *fmt
= "%a %b %d %H:%M %Y";
4666 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4668 if(giveownership
->staff_issuer
)
4670 if(giveownership
->reason
)
4671 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4672 giveownership
->target
, giveownership
->target_access
,
4673 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4675 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4676 giveownership
->target
, giveownership
->target_access
,
4677 giveownership
->staff_issuer
, buf
);
4681 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4686 static CHANSERV_FUNC(cmd_info
)
4688 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4689 struct userData
*uData
, *owner
;
4690 struct chanData
*cData
;
4691 struct do_not_register
*dnr
;
4696 cData
= channel
->channel_info
;
4697 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4698 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4701 uData
= GetChannelUser(cData
, user
->handle_info
);
4702 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4704 mod_chanmode_format(&cData
->modes
, modes
);
4705 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4706 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4709 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4713 note
= iter_data(it
);
4714 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4717 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4718 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4721 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4722 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4723 if(owner
->access
== UL_OWNER
)
4724 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4725 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4726 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4727 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4729 privileged
= IsStaff(user
);
4730 /* if(privileged) */
4731 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4732 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4733 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4735 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4736 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4738 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4740 struct suspended
*suspended
;
4741 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4742 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4743 show_suspension_info(cmd
, user
, suspended
);
4745 else if(IsSuspended(cData
))
4747 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4748 show_suspension_info(cmd
, user
, cData
->suspended
);
4750 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4752 struct giveownership
*giveownership
;
4753 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4754 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4755 show_giveownership_info(cmd
, user
, giveownership
);
4757 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4758 reply("CSMSG_CHANNEL_END");
4760 reply("CSMSG_CHANNEL_END_CLEAN");
4764 static CHANSERV_FUNC(cmd_netinfo
)
4766 extern time_t boot_time
;
4767 extern unsigned long burst_length
;
4768 char interval
[INTERVALLEN
];
4770 reply("CSMSG_NETWORK_INFO");
4771 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4772 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4773 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4774 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4775 reply("CSMSG_NETWORK_LAMERS", banCount
);
4776 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4777 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4778 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4783 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4785 struct helpfile_table table
;
4787 struct userNode
*user
;
4792 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4793 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4794 for(nn
=0; nn
<list
->used
; nn
++)
4796 user
= list
->list
[nn
];
4797 if(user
->modes
& skip_flags
)
4801 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4804 nick
= alloca(strlen(user
->nick
)+3);
4805 sprintf(nick
, "(%s)", user
->nick
);
4809 table
.contents
[table
.length
][0] = nick
;
4812 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4815 static CHANSERV_FUNC(cmd_ircops
)
4817 reply("CSMSG_STAFF_OPERS");
4818 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4822 static CHANSERV_FUNC(cmd_helpers
)
4824 reply("CSMSG_STAFF_HELPERS");
4825 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4829 static CHANSERV_FUNC(cmd_staff
)
4831 reply("CSMSG_NETWORK_STAFF");
4832 cmd_ircops(CSFUNC_ARGS
);
4833 cmd_helpers(CSFUNC_ARGS
);
4837 static CHANSERV_FUNC(cmd_peek
)
4839 struct modeNode
*mn
;
4840 char modes
[MODELEN
];
4842 struct helpfile_table table
;
4844 irc_make_chanmode(channel
, modes
);
4846 reply("CSMSG_PEEK_INFO", channel
->name
);
4847 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4849 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4850 reply("CSMSG_PEEK_MODES", modes
);
4851 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4855 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4856 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4857 for(n
= 0; n
< channel
->members
.used
; n
++)
4859 mn
= channel
->members
.list
[n
];
4860 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4862 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4863 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4868 reply("CSMSG_PEEK_OPS");
4869 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4872 reply("CSMSG_PEEK_NO_OPS");
4873 reply("CSMSG_PEEK_END");
4877 static MODCMD_FUNC(cmd_wipeinfo
)
4879 struct handle_info
*victim
;
4880 struct userData
*ud
, *actor
;
4883 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4884 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4886 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4888 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4891 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4893 reply("MSG_USER_OUTRANKED", victim
->handle
);
4899 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4904 resync_channel(struct chanNode
*channel
)
4906 struct mod_chanmode
*changes
;
4907 struct chanData
*cData
= channel
->channel_info
;
4908 unsigned int ii
, used
;
4910 /* 6 = worst case -ovh+ovh on everyone */
4911 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4912 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4914 struct modeNode
*mn
= channel
->members
.list
[ii
];
4915 struct userData
*uData
;
4917 if(IsService(mn
->user
))
4921 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4923 /* If the channel is in no-mode mode, de-mode EVERYONE */
4924 if(cData
->chOpts
[chAutomode
] == 'n')
4928 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4929 changes
->args
[used
++].u
.member
= mn
;
4932 else /* Give various userlevels their modes.. */
4934 if(uData
&& uData
->access
>= UL_OP
)
4936 if(!(mn
->modes
& MODE_CHANOP
))
4938 changes
->args
[used
].mode
= MODE_CHANOP
;
4939 changes
->args
[used
++].u
.member
= mn
;
4942 else if(uData
&& uData
->access
>= UL_HALFOP
)
4944 if(mn
->modes
& MODE_CHANOP
)
4946 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4947 changes
->args
[used
++].u
.member
= mn
;
4949 if(!(mn
->modes
& MODE_HALFOP
))
4951 changes
->args
[used
].mode
= MODE_HALFOP
;
4952 changes
->args
[used
++].u
.member
= mn
;
4955 else if(uData
&& uData
->access
>= UL_PEON
)
4957 if(mn
->modes
& MODE_CHANOP
)
4959 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4960 changes
->args
[used
++].u
.member
= mn
;
4962 if(mn
->modes
& MODE_HALFOP
)
4964 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4965 changes
->args
[used
++].u
.member
= mn
;
4967 /* Don't voice peons if were in mode m */
4968 if( cData
->chOpts
[chAutomode
] == 'm')
4970 if(mn
->modes
& MODE_VOICE
)
4972 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4973 changes
->args
[used
++].u
.member
= mn
;
4976 /* otherwise, make user they do have voice */
4977 else if(!(mn
->modes
& MODE_VOICE
))
4979 changes
->args
[used
].mode
= MODE_VOICE
;
4980 changes
->args
[used
++].u
.member
= mn
;
4983 else /* They arnt on the userlist.. */
4985 /* If we voice everyone, but they dont.. */
4986 if(cData
->chOpts
[chAutomode
] == 'v')
4988 /* Remove anything except v */
4989 if(mn
->modes
& ~MODE_VOICE
)
4991 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4992 changes
->args
[used
++].u
.member
= mn
;
4995 if(!(mn
->modes
& MODE_VOICE
))
4997 changes
->args
[used
].mode
= MODE_VOICE
;
4998 changes
->args
[used
++].u
.member
= mn
;
5001 /* If we hop everyone, but they dont.. */
5002 else if(cData
->chOpts
[chAutomode
] == 'h')
5004 /* Remove anything except h */
5005 if(mn
->modes
& ~MODE_HALFOP
)
5007 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
5008 changes
->args
[used
++].u
.member
= mn
;
5011 if(!(mn
->modes
& MODE_HALFOP
))
5013 changes
->args
[used
].mode
= MODE_HALFOP
;
5014 changes
->args
[used
++].u
.member
= mn
;
5017 /* If we op everyone, but they dont.. */
5018 else if(cData
->chOpts
[chAutomode
] == 'o')
5020 /* Remove anything except h */
5021 if(mn
->modes
& ~MODE_CHANOP
)
5023 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
5024 changes
->args
[used
++].u
.member
= mn
;
5027 if(!(mn
->modes
& MODE_CHANOP
))
5029 changes
->args
[used
].mode
= MODE_CHANOP
;
5030 changes
->args
[used
++].u
.member
= mn
;
5033 /* they have no excuse for having modes, de-everything them */
5038 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5039 changes
->args
[used
++].u
.member
= mn
;
5045 changes
->argc
= used
;
5046 mod_chanmode_announce(chanserv
, channel
, changes
);
5047 mod_chanmode_free(changes
);
5050 static CHANSERV_FUNC(cmd_resync
)
5052 resync_channel(channel
);
5053 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5057 static CHANSERV_FUNC(cmd_seen
)
5059 struct userData
*uData
;
5060 struct handle_info
*handle
;
5061 char seen
[INTERVALLEN
];
5065 if(!irccasecmp(argv
[1], chanserv
->nick
))
5067 reply("CSMSG_IS_CHANSERV");
5071 if(!(handle
= get_handle_info(argv
[1])))
5073 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5077 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5079 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5084 reply("CSMSG_USER_PRESENT", handle
->handle
);
5085 else if(uData
->seen
)
5086 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5088 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5090 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5091 reply("CSMSG_USER_VACATION", handle
->handle
);
5096 static MODCMD_FUNC(cmd_names
)
5098 struct userNode
*targ
;
5099 struct userData
*targData
;
5100 unsigned int ii
, pos
;
5103 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5105 targ
= channel
->members
.list
[ii
]->user
;
5106 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5109 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5112 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5116 if(IsUserSuspended(targData
))
5118 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5121 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5122 reply("CSMSG_END_NAMES", channel
->name
);
5127 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5129 switch(ntype
->visible_type
)
5131 case NOTE_VIS_ALL
: return 1;
5132 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5133 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5138 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5140 struct userData
*uData
;
5142 switch(ntype
->set_access_type
)
5144 case NOTE_SET_CHANNEL_ACCESS
:
5145 if(!user
->handle_info
)
5147 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5149 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5150 case NOTE_SET_CHANNEL_SETTER
:
5151 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5152 case NOTE_SET_PRIVILEGED
: default:
5153 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5157 static CHANSERV_FUNC(cmd_note
)
5159 struct chanData
*cData
;
5161 struct note_type
*ntype
;
5163 cData
= channel
->channel_info
;
5166 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5170 /* If no arguments, show all visible notes for the channel. */
5176 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5178 note
= iter_data(it
);
5179 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5182 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5183 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5186 reply("CSMSG_NOTELIST_END", channel
->name
);
5188 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5190 /* If one argument, show the named note. */
5193 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5194 && note_type_visible_to_user(cData
, note
->type
, user
))
5196 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5198 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5199 && note_type_visible_to_user(NULL
, ntype
, user
))
5201 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5206 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5210 /* Assume they're trying to set a note. */
5214 ntype
= dict_find(note_types
, argv
[1], NULL
);
5217 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5220 else if(note_type_settable_by_user(channel
, ntype
, user
))
5222 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5223 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5224 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5225 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5226 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5228 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5230 /* The note is viewable to staff only, so return 0
5231 to keep the invocation from getting logged (or
5232 regular users can see it in !events). */
5238 reply("CSMSG_NO_ACCESS");
5245 static CHANSERV_FUNC(cmd_delnote
)
5250 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5251 || !note_type_settable_by_user(channel
, note
->type
, user
))
5253 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5256 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5257 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5261 static CHANSERV_FUNC(cmd_last
)
5267 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5269 if(numoflines
< 1 || numoflines
> 200)
5271 reply("CSMSG_LAST_INVALID");
5274 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5278 static CHANSERV_FUNC(cmd_events
)
5280 struct logSearch discrim
;
5281 struct logReport report
;
5282 unsigned int matches
, limit
;
5284 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5285 if(limit
< 1 || limit
> 200)
5288 memset(&discrim
, 0, sizeof(discrim
));
5289 discrim
.masks
.bot
= chanserv
;
5290 discrim
.masks
.channel_name
= channel
->name
;
5292 discrim
.masks
.command
= argv
[2];
5293 discrim
.limit
= limit
;
5294 discrim
.max_time
= INT_MAX
;
5295 discrim
.severities
= 1 << LOG_COMMAND
;
5296 report
.reporter
= chanserv
;
5298 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5299 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5301 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5303 reply("MSG_MATCH_COUNT", matches
);
5305 reply("MSG_NO_MATCHES");
5309 static CHANSERV_FUNC(cmd_say
)
5315 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5316 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5318 else if(GetUserH(argv
[1]))
5321 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5322 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5326 reply("MSG_NOT_TARGET_NAME");
5332 static CHANSERV_FUNC(cmd_emote
)
5338 /* CTCP is so annoying. */
5339 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5340 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5342 else if(GetUserH(argv
[1]))
5344 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5345 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5349 reply("MSG_NOT_TARGET_NAME");
5355 struct channelList
*
5356 chanserv_support_channels(void)
5358 return &chanserv_conf
.support_channels
;
5361 static CHANSERV_FUNC(cmd_expire
)
5363 int channel_count
= registered_channels
;
5364 expire_channels(NULL
);
5365 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5370 chanserv_expire_suspension(void *data
)
5372 struct suspended
*suspended
= data
;
5373 struct chanNode
*channel
;
5375 if(!suspended
->expires
|| (now
< suspended
->expires
))
5376 suspended
->revoked
= now
;
5377 channel
= suspended
->cData
->channel
;
5378 suspended
->cData
->channel
= channel
;
5379 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5380 if(!IsOffChannel(suspended
->cData
))
5382 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5383 ss_cs_join_channel(channel
, 1);
5387 static CHANSERV_FUNC(cmd_csuspend
)
5389 struct suspended
*suspended
;
5390 char reason
[MAXLEN
];
5391 time_t expiry
, duration
;
5392 struct userData
*uData
;
5396 if(IsProtected(channel
->channel_info
))
5398 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5402 if(argv
[1][0] == '!')
5404 else if(IsSuspended(channel
->channel_info
))
5406 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5407 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5411 if(!strcmp(argv
[1], "0"))
5413 else if((duration
= ParseInterval(argv
[1])))
5414 expiry
= now
+ duration
;
5417 reply("MSG_INVALID_DURATION", argv
[1]);
5421 unsplit_string(argv
+ 2, argc
- 2, reason
);
5423 suspended
= calloc(1, sizeof(*suspended
));
5424 suspended
->revoked
= 0;
5425 suspended
->issued
= now
;
5426 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5427 suspended
->expires
= expiry
;
5428 suspended
->reason
= strdup(reason
);
5429 suspended
->cData
= channel
->channel_info
;
5430 suspended
->previous
= suspended
->cData
->suspended
;
5431 suspended
->cData
->suspended
= suspended
;
5433 if(suspended
->expires
)
5434 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5436 if(IsSuspended(channel
->channel_info
))
5438 suspended
->previous
->revoked
= now
;
5439 if(suspended
->previous
->expires
)
5440 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5442 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5443 channel
->name
, suspended
->suspender
);
5447 /* Mark all users in channel as absent. */
5448 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5457 /* Mark the channel as suspended, then part. */
5458 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5459 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5460 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5461 reply("CSMSG_SUSPENDED", channel
->name
);
5462 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5463 channel
->name
, suspended
->suspender
);
5468 static CHANSERV_FUNC(cmd_cunsuspend
)
5470 struct suspended
*suspended
;
5472 if(!IsSuspended(channel
->channel_info
))
5474 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5478 suspended
= channel
->channel_info
->suspended
;
5480 /* Expire the suspension and join ChanServ to the channel. */
5481 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5482 chanserv_expire_suspension(suspended
);
5483 reply("CSMSG_UNSUSPENDED", channel
->name
);
5484 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5485 channel
->name
, user
->handle_info
->handle
);
5489 typedef struct chanservSearch
5497 unsigned long flags
;
5501 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5504 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5509 search
= malloc(sizeof(struct chanservSearch
));
5510 memset(search
, 0, sizeof(*search
));
5513 for(i
= 0; i
< argc
; i
++)
5515 /* Assume all criteria require arguments. */
5518 reply("MSG_MISSING_PARAMS", argv
[i
]);
5522 if(!irccasecmp(argv
[i
], "name"))
5523 search
->name
= argv
[++i
];
5524 else if(!irccasecmp(argv
[i
], "registrar"))
5525 search
->registrar
= argv
[++i
];
5526 else if(!irccasecmp(argv
[i
], "unvisited"))
5527 search
->unvisited
= ParseInterval(argv
[++i
]);
5528 else if(!irccasecmp(argv
[i
], "registered"))
5529 search
->registered
= ParseInterval(argv
[++i
]);
5530 else if(!irccasecmp(argv
[i
], "flags"))
5533 if(!irccasecmp(argv
[i
], "nodelete"))
5534 search
->flags
|= CHANNEL_NODELETE
;
5535 else if(!irccasecmp(argv
[i
], "suspended"))
5536 search
->flags
|= CHANNEL_SUSPENDED
;
5539 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5543 else if(!irccasecmp(argv
[i
], "limit"))
5544 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5547 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5552 if(search
->name
&& !strcmp(search
->name
, "*"))
5554 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5555 search
->registrar
= 0;
5564 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5566 const char *name
= channel
->channel
->name
;
5567 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5568 (search
->registrar
&& !channel
->registrar
) ||
5569 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5570 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5571 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5572 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5579 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5581 struct chanData
*channel
;
5582 unsigned int matches
= 0;
5584 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5586 if(!chanserv_channel_match(channel
, search
))
5596 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5601 search_print(struct chanData
*channel
, void *data
)
5603 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5606 static CHANSERV_FUNC(cmd_search
)
5609 unsigned int matches
;
5610 channel_search_func action
;
5614 if(!irccasecmp(argv
[1], "count"))
5615 action
= search_count
;
5616 else if(!irccasecmp(argv
[1], "print"))
5617 action
= search_print
;
5620 reply("CSMSG_ACTION_INVALID", argv
[1]);
5624 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5628 if(action
== search_count
)
5629 search
->limit
= INT_MAX
;
5631 if(action
== search_print
)
5633 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5634 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5638 matches
= chanserv_channel_search(search
, action
, user
);
5641 reply("MSG_MATCH_COUNT", matches
);
5643 reply("MSG_NO_MATCHES");
5649 static CHANSERV_FUNC(cmd_unvisited
)
5651 struct chanData
*cData
;
5652 time_t interval
= chanserv_conf
.channel_expire_delay
;
5653 char buffer
[INTERVALLEN
];
5654 unsigned int limit
= 25, matches
= 0;
5658 interval
= ParseInterval(argv
[1]);
5660 limit
= atoi(argv
[2]);
5663 intervalString(buffer
, interval
, user
->handle_info
);
5664 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5666 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5668 if((now
- cData
->visited
) < interval
)
5671 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5672 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5679 static MODCMD_FUNC(chan_opt_defaulttopic
)
5685 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5687 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5691 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5693 free(channel
->channel_info
->topic
);
5694 if(topic
[0] == '*' && topic
[1] == 0)
5696 topic
= channel
->channel_info
->topic
= NULL
;
5700 topic
= channel
->channel_info
->topic
= strdup(topic
);
5701 if(channel
->channel_info
->topic_mask
5702 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5703 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5705 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5708 if(channel
->channel_info
->topic
)
5709 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5711 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5715 static MODCMD_FUNC(chan_opt_topicmask
)
5719 struct chanData
*cData
= channel
->channel_info
;
5722 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5724 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5728 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5730 if(cData
->topic_mask
)
5731 free(cData
->topic_mask
);
5732 if(mask
[0] == '*' && mask
[1] == 0)
5734 cData
->topic_mask
= 0;
5738 cData
->topic_mask
= strdup(mask
);
5740 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5741 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5742 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5746 if(channel
->channel_info
->topic_mask
)
5747 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5749 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5753 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5757 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5761 if(greeting
[0] == '*' && greeting
[1] == 0)
5765 unsigned int length
= strlen(greeting
);
5766 if(length
> chanserv_conf
.greeting_length
)
5768 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5771 *data
= strdup(greeting
);
5780 reply(name
, user_find_message(user
, "MSG_NONE"));
5784 static MODCMD_FUNC(chan_opt_greeting
)
5786 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5789 static MODCMD_FUNC(chan_opt_usergreeting
)
5791 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5794 static MODCMD_FUNC(chan_opt_modes
)
5796 struct mod_chanmode
*new_modes
;
5797 char modes
[MODELEN
];
5801 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5802 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5806 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5808 reply("CSMSG_NO_ACCESS");
5811 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5813 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5815 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5817 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5820 else if(new_modes
->argc
> 1)
5822 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5823 mod_chanmode_free(new_modes
);
5828 channel
->channel_info
->modes
= *new_modes
;
5829 modcmd_chanmode_announce(new_modes
);
5830 mod_chanmode_free(new_modes
);
5834 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5836 reply("CSMSG_SET_MODES", modes
);
5838 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5842 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5844 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5846 struct chanData
*cData
= channel
->channel_info
;
5851 /* Set flag according to value. */
5852 if(enabled_string(argv
[1]))
5854 cData
->flags
|= mask
;
5857 else if(disabled_string(argv
[1]))
5859 cData
->flags
&= ~mask
;
5864 reply("MSG_INVALID_BINARY", argv
[1]);
5870 /* Find current option value. */
5871 value
= (cData
->flags
& mask
) ? 1 : 0;
5875 reply(name
, user_find_message(user
, "MSG_ON"));
5877 reply(name
, user_find_message(user
, "MSG_OFF"));
5881 static MODCMD_FUNC(chan_opt_nodelete
)
5883 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5885 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5889 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5892 static MODCMD_FUNC(chan_opt_dynlimit
)
5894 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5897 static MODCMD_FUNC(chan_opt_offchannel
)
5899 struct chanData
*cData
= channel
->channel_info
;
5904 /* Set flag according to value. */
5905 if(enabled_string(argv
[1]))
5907 if(!IsOffChannel(cData
))
5908 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5909 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5912 else if(disabled_string(argv
[1]))
5914 if(IsOffChannel(cData
))
5916 struct mod_chanmode change
;
5917 mod_chanmode_init(&change
);
5919 change
.args
[0].mode
= MODE_CHANOP
;
5920 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5921 mod_chanmode_announce(chanserv
, channel
, &change
);
5923 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5928 reply("MSG_INVALID_BINARY", argv
[1]);
5934 /* Find current option value. */
5935 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5939 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5941 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5945 static MODCMD_FUNC(chan_opt_defaults
)
5947 struct userData
*uData
;
5948 struct chanData
*cData
;
5949 const char *confirm
;
5950 enum levelOption lvlOpt
;
5951 enum charOption chOpt
;
5953 cData
= channel
->channel_info
;
5954 uData
= GetChannelUser(cData
, user
->handle_info
);
5955 if(!uData
|| (uData
->access
< UL_OWNER
))
5957 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5960 confirm
= make_confirmation_string(uData
);
5961 if((argc
< 2) || strcmp(argv
[1], confirm
))
5963 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5966 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5967 cData
->modes
= chanserv_conf
.default_modes
;
5968 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5969 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5970 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5971 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5972 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5977 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5979 struct chanData
*cData
= channel
->channel_info
;
5980 struct userData
*uData
;
5981 unsigned short value
;
5985 if(!check_user_level(channel
, user
, option
, 1, 1))
5987 reply("CSMSG_CANNOT_SET");
5990 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5991 if(!value
&& strcmp(argv
[1], "0"))
5993 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5996 uData
= GetChannelUser(cData
, user
->handle_info
);
5997 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5999 reply("CSMSG_BAD_SETLEVEL");
6005 /* This test only applies to owners, since non-owners
6006 * trying to set an option to above their level get caught
6007 * by the CSMSG_BAD_SETLEVEL test above.
6009 if(value
> uData
->access
)
6011 reply("CSMSG_BAD_SETTERS");
6018 cData
->lvlOpts
[option
] = value
;
6020 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
6024 static MODCMD_FUNC(chan_opt_enfops
)
6026 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
6029 static MODCMD_FUNC(chan_opt_enfhalfops
)
6031 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6033 static MODCMD_FUNC(chan_opt_enfmodes
)
6035 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6038 static MODCMD_FUNC(chan_opt_enftopic
)
6040 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6043 static MODCMD_FUNC(chan_opt_pubcmd
)
6045 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6048 static MODCMD_FUNC(chan_opt_setters
)
6050 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6053 static MODCMD_FUNC(chan_opt_userinfo
)
6055 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6058 static MODCMD_FUNC(chan_opt_topicsnarf
)
6060 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6063 static MODCMD_FUNC(chan_opt_inviteme
)
6065 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6068 /* TODO: Make look like this when no args are
6070 * -X3- -------------------------------
6071 * -X3- BanTimeout: Bans are removed:
6072 * -X3- ----- * indicates current -----
6073 * -X3- 0: [*] Never.
6074 * -X3- 1: [ ] After 10 minutes.
6075 * -X3- 2: [ ] After 2 hours.
6076 * -X3- 3: [ ] After 4 hours.
6077 * -X3- 4: [ ] After 24 hours.
6078 * -X3- 5: [ ] After one week.
6079 * -X3- ------------- End -------------
6082 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6084 struct chanData
*cData
= channel
->channel_info
;
6085 int count
= charOptions
[option
].count
, index
;
6089 index
= atoi(argv
[1]);
6091 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6093 reply("CSMSG_INVALID_NUMERIC", index
);
6094 /* Show possible values. */
6095 for(index
= 0; index
< count
; index
++)
6096 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6100 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6104 /* Find current option value. */
6107 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6111 /* Somehow, the option value is corrupt; reset it to the default. */
6112 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6117 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6121 static MODCMD_FUNC(chan_opt_automode
)
6123 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6126 static MODCMD_FUNC(chan_opt_protect
)
6128 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6131 static MODCMD_FUNC(chan_opt_toys
)
6133 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6136 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6138 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6141 static MODCMD_FUNC(chan_opt_bantimeout
)
6143 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6146 static MODCMD_FUNC(chan_opt_topicrefresh
)
6148 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6151 static MODCMD_FUNC(chan_opt_resync
)
6153 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6156 static struct svccmd_list set_shows_list
;
6159 handle_svccmd_unbind(struct svccmd
*target
) {
6161 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6162 if(target
== set_shows_list
.list
[ii
])
6163 set_shows_list
.used
= 0;
6166 static CHANSERV_FUNC(cmd_set
)
6168 struct svccmd
*subcmd
;
6172 /* Check if we need to (re-)initialize set_shows_list. */
6173 if(!set_shows_list
.used
)
6175 if(!set_shows_list
.size
)
6177 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6178 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6180 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6182 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6183 sprintf(buf
, "%s %s", argv
[0], name
);
6184 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6187 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6190 svccmd_list_append(&set_shows_list
, subcmd
);
6196 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6197 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6199 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6201 subcmd
= set_shows_list
.list
[ii
];
6202 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6204 reply("CSMSG_CHANNEL_OPTIONS_END");
6208 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6209 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6212 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6215 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6217 reply("CSMSG_NO_ACCESS");
6221 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6225 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6227 struct userData
*uData
;
6229 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6232 reply("CSMSG_NOT_USER", channel
->name
);
6238 /* Just show current option value. */
6240 else if(enabled_string(argv
[1]))
6242 uData
->flags
|= mask
;
6244 else if(disabled_string(argv
[1]))
6246 uData
->flags
&= ~mask
;
6250 reply("MSG_INVALID_BINARY", argv
[1]);
6254 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6258 static MODCMD_FUNC(user_opt_autoop
)
6260 struct userData
*uData
;
6262 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6265 reply("CSMSG_NOT_USER", channel
->name
);
6268 if(uData
->access
< UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6269 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6271 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6274 static MODCMD_FUNC(user_opt_autoinvite
)
6276 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6279 static MODCMD_FUNC(user_opt_autojoin
)
6281 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6284 static MODCMD_FUNC(user_opt_info
)
6286 struct userData
*uData
;
6289 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6293 /* If they got past the command restrictions (which require access)
6294 * but fail this test, we have some fool with security override on.
6296 reply("CSMSG_NOT_USER", channel
->name
);
6303 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6304 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6306 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6309 bp
= strcspn(infoline
, "\001");
6312 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6317 if(infoline
[0] == '*' && infoline
[1] == 0)
6320 uData
->info
= strdup(infoline
);
6323 reply("CSMSG_USET_INFO", uData
->info
);
6325 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6329 struct svccmd_list uset_shows_list
;
6331 static CHANSERV_FUNC(cmd_uset
)
6333 struct svccmd
*subcmd
;
6337 /* Check if we need to (re-)initialize uset_shows_list. */
6338 if(!uset_shows_list
.used
)
6342 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6345 if(!uset_shows_list
.size
)
6347 uset_shows_list
.size
= ArrayLength(options
);
6348 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6350 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6352 const char *name
= options
[ii
];
6353 sprintf(buf
, "%s %s", argv
[0], name
);
6354 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6357 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6360 svccmd_list_append(&uset_shows_list
, subcmd
);
6366 /* Do this so options are presented in a consistent order. */
6367 reply("CSMSG_USER_OPTIONS");
6368 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6369 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6373 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6374 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6377 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6381 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6384 static CHANSERV_FUNC(cmd_giveownership
)
6386 struct handle_info
*new_owner_hi
;
6387 struct userData
*new_owner
, *curr_user
;
6388 struct chanData
*cData
= channel
->channel_info
;
6389 struct do_not_register
*dnr
;
6390 struct giveownership
*giveownership
;
6391 unsigned int force
, override
;
6392 unsigned short co_access
, new_owner_old_access
;
6393 char transfer_reason
[MAXLEN
];
6396 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6397 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6399 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6400 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6401 && (uData
->access
> 500)
6402 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6403 || uData
->access
< 500));
6406 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6408 struct userData
*owner
= NULL
;
6409 for(curr_user
= channel
->channel_info
->users
;
6411 curr_user
= curr_user
->next
)
6413 if(curr_user
->access
!= UL_OWNER
)
6417 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6424 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6426 char delay
[INTERVALLEN
];
6427 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6428 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6432 reply("CSMSG_NO_OWNER", channel
->name
);
6435 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6437 if(new_owner_hi
== user
->handle_info
)
6439 reply("CSMSG_NO_TRANSFER_SELF");
6442 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6447 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6451 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6455 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6457 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6460 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6461 if(!IsHelping(user
))
6462 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6464 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6468 new_owner_old_access
= new_owner
->access
;
6469 if(new_owner
->access
>= UL_COOWNER
)
6470 co_access
= new_owner
->access
;
6472 co_access
= UL_COOWNER
;
6473 new_owner
->access
= UL_OWNER
;
6475 curr_user
->access
= co_access
;
6476 cData
->ownerTransfer
= now
;
6478 giveownership
= calloc(1, sizeof(*giveownership
));
6479 giveownership
->issued
= now
;
6480 giveownership
->old_owner
= curr_user
->handle
->handle
;
6481 giveownership
->target
= new_owner_hi
->handle
;
6482 giveownership
->target_access
= new_owner_old_access
;
6485 if(argc
> (2 + force
))
6487 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6488 giveownership
->reason
= strdup(transfer_reason
);
6490 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6493 giveownership
->previous
= channel
->channel_info
->giveownership
;
6494 channel
->channel_info
->giveownership
= giveownership
;
6496 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6497 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6498 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6503 chanserv_expire_user_suspension(void *data
)
6505 struct userData
*target
= data
;
6507 target
->expires
= 0;
6508 target
->flags
&= ~USER_SUSPENDED
;
6511 static CHANSERV_FUNC(cmd_suspend
)
6513 struct handle_info
*hi
;
6514 struct userData
*self
, *target
;
6518 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6519 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6520 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6522 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6525 if(target
->access
>= self
->access
)
6527 reply("MSG_USER_OUTRANKED", hi
->handle
);
6530 if(target
->flags
& USER_SUSPENDED
)
6532 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6537 target
->present
= 0;
6540 if(!strcmp(argv
[2], "0"))
6544 unsigned int duration
;
6545 if(!(duration
= ParseInterval(argv
[2])))
6547 reply("MSG_INVALID_DURATION", argv
[2]);
6550 expiry
= now
+ duration
;
6553 target
->expires
= expiry
;
6556 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6558 target
->flags
|= USER_SUSPENDED
;
6559 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6563 static CHANSERV_FUNC(cmd_unsuspend
)
6565 struct handle_info
*hi
;
6566 struct userData
*self
, *target
;
6569 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6570 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6571 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6573 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6576 if(target
->access
>= self
->access
)
6578 reply("MSG_USER_OUTRANKED", hi
->handle
);
6581 if(!(target
->flags
& USER_SUSPENDED
))
6583 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6586 target
->flags
&= ~USER_SUSPENDED
;
6587 scan_user_presence(target
, NULL
);
6588 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6589 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6593 static MODCMD_FUNC(cmd_deleteme
)
6595 struct handle_info
*hi
;
6596 struct userData
*target
;
6597 const char *confirm_string
;
6598 unsigned short access
;
6601 hi
= user
->handle_info
;
6602 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6604 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6607 if(target
->access
== UL_OWNER
)
6609 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6612 confirm_string
= make_confirmation_string(target
);
6613 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6615 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6618 access
= target
->access
;
6619 channel_name
= strdup(channel
->name
);
6620 del_channel_user(target
, 1);
6621 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6627 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6629 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6630 struct chanData
*cData
;
6633 for(cData
= channelList
; cData
; cData
= cData
->next
)
6635 if(IsSuspended(cData
))
6637 opt
= cData
->chOpts
[chTopicRefresh
];
6640 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6643 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6644 cData
->last_refresh
= refresh_num
;
6646 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6650 chanserv_auto_resync(UNUSED_ARG(void *data
))
6652 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6653 struct chanData
*cData
;
6656 for(cData
= channelList
; cData
; cData
= cData
->next
)
6658 if(IsSuspended(cData
)) continue;
6659 opt
= cData
->chOpts
[chResync
];
6660 if(opt
== 'n') continue;
6661 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6662 resync_channel(cData
->channel
);
6663 cData
->last_resync
= refresh_num
;
6665 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6668 static CHANSERV_FUNC(cmd_unf
)
6672 char response
[MAXLEN
];
6673 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6674 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6675 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6678 reply("CSMSG_UNF_RESPONSE");
6682 static CHANSERV_FUNC(cmd_ping
)
6686 char response
[MAXLEN
];
6687 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6688 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6689 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6692 reply("CSMSG_PING_RESPONSE");
6696 static CHANSERV_FUNC(cmd_wut
)
6700 char response
[MAXLEN
];
6701 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6702 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6703 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6706 reply("CSMSG_WUT_RESPONSE");
6711 static CHANSERV_FUNC(cmd_8ball
)
6713 unsigned int i
, j
, accum
;
6718 for(i
=1; i
<argc
; i
++)
6719 for(j
=0; argv
[i
][j
]; j
++)
6720 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6721 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6724 char response
[MAXLEN
];
6725 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6726 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6729 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6733 #else /* Use cool 8ball instead */
6735 void eightball(char *outcome
, int method
, unsigned int seed
)
6739 #define NUMOFCOLORS 18
6740 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6741 "white", "black", "grey", "brown",
6742 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6743 "fuchsia","turquoise","magenta", "cyan"};
6744 #define NUMOFLOCATIONS 50
6745 char balllocations
[50][55] = {
6746 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6747 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6748 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6749 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6750 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6751 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6752 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6753 "your bra", "your hair", "your bed", "the couch", "the wall",
6754 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6755 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6756 #define NUMOFPREPS 15
6757 char ballpreps
[50][50] = {
6758 "Near", "Somewhere near", "In", "In", "In",
6759 "In", "Hiding in", "Under", "Next to", "Over",
6760 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6761 #define NUMOFNUMS 34
6762 char ballnums
[50][50] = {
6763 "A hundred", "A thousand", "A few", "42",
6764 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6765 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6766 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6768 #define NUMOFMULTS 8
6769 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6772 * 0: normal (Not used in x3)
6779 if (method
== 1) /* A Color */
6783 answer
= (rand() % 12); /* Make sure this is the # of entries */
6786 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6788 case 1: strcpy(tmp
, "Sort of a light %s color.");
6790 case 2: strcpy(tmp
, "Dark and dreary %s.");
6792 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6794 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6796 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6798 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6800 case 10: strcpy(tmp
, "Solid %s.");
6802 case 11: strcpy(tmp
, "Transparent %s.");
6804 default: strcpy(outcome
, "An invalid random number was generated.");
6807 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6810 else if (method
== 2) /* Location */
6812 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6814 else if (method
== 3) /* Number of ___ */
6816 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6820 //Debug(DBGWARNING, "Error in 8ball.");
6825 static CHANSERV_FUNC(cmd_8ball
)
6827 char *word1
, *word2
, *word3
;
6828 static char eb
[MAXLEN
];
6829 unsigned int accum
, i
, j
;
6833 for(i
=1; i
<argc
; i
++)
6834 for(j
=0; argv
[i
][j
]; j
++)
6835 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6837 accum
+= time(NULL
)/3600;
6839 word2
= argc
>2?argv
[2]:"";
6840 word3
= argc
>3?argv
[3]:"";
6843 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6844 eightball(eb
, 1, accum
);
6845 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6846 eightball(eb
, 1, accum
);
6847 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6848 eightball(eb
, 1, accum
);
6849 /*** LOCATION *****/
6854 (strcasecmp(word1
, "where") == 0) &&
6855 (strcasecmp(word2
, "is") == 0)
6859 strcasecmp(word1
, "where's") == 0
6862 eightball(eb
, 2, accum
);
6864 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6865 eightball(eb
, 3, accum
);
6869 /* Generic 8ball question.. so pull from x3.conf srvx style */
6872 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6875 char response
[MAXLEN
];
6876 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6877 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6880 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6886 char response
[MAXLEN
];
6887 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6888 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6891 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6896 static CHANSERV_FUNC(cmd_d
)
6898 unsigned long sides
, count
, modifier
, ii
, total
;
6899 char response
[MAXLEN
], *sep
;
6903 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6913 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6914 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6918 else if((sep
[0] == '-') && isdigit(sep
[1]))
6919 modifier
= strtoul(sep
, NULL
, 10);
6920 else if((sep
[0] == '+') && isdigit(sep
[1]))
6921 modifier
= strtoul(sep
+1, NULL
, 10);
6928 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6933 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6936 for(total
= ii
= 0; ii
< count
; ++ii
)
6937 total
+= (rand() % sides
) + 1;
6940 if((count
> 1) || modifier
)
6942 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6943 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6947 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6948 sprintf(response
, fmt
, total
, sides
);
6951 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6953 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6957 static CHANSERV_FUNC(cmd_huggle
)
6959 /* CTCP must be via PRIVMSG, never notice */
6961 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6963 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6967 static CHANSERV_FUNC(cmd_calc
)
6969 char response
[MAXLEN
];
6972 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6975 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6977 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6981 static CHANSERV_FUNC(cmd_reply
)
6985 unsplit_string(argv
+ 1, argc
- 1, NULL
);
6988 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6990 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
6995 chanserv_adjust_limit(void *data
)
6997 struct mod_chanmode change
;
6998 struct chanData
*cData
= data
;
6999 struct chanNode
*channel
= cData
->channel
;
7002 if(IsSuspended(cData
))
7005 cData
->limitAdjusted
= now
;
7006 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
7007 if(cData
->modes
.modes_set
& MODE_LIMIT
)
7009 if(limit
> cData
->modes
.new_limit
)
7010 limit
= cData
->modes
.new_limit
;
7011 else if(limit
== cData
->modes
.new_limit
)
7015 mod_chanmode_init(&change
);
7016 change
.modes_set
= MODE_LIMIT
;
7017 change
.new_limit
= limit
;
7018 mod_chanmode_announce(chanserv
, channel
, &change
);
7022 handle_new_channel(struct chanNode
*channel
)
7024 struct chanData
*cData
;
7026 if(!(cData
= channel
->channel_info
))
7029 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7030 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7032 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7033 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7036 /* Welcome to my worst nightmare. Warning: Read (or modify)
7037 the code below at your own risk. */
7039 handle_join(struct modeNode
*mNode
)
7041 struct mod_chanmode change
;
7042 struct userNode
*user
= mNode
->user
;
7043 struct chanNode
*channel
= mNode
->channel
;
7044 struct chanData
*cData
;
7045 struct userData
*uData
= NULL
;
7046 struct banData
*bData
;
7047 struct handle_info
*handle
;
7048 unsigned int modes
= 0, info
= 0;
7051 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7054 cData
= channel
->channel_info
;
7055 if(channel
->members
.used
> cData
->max
)
7056 cData
->max
= channel
->members
.used
;
7059 /* Check for bans. If they're joining through a ban, one of two
7061 * 1: Join during a netburst, by riding the break. Kick them
7062 * unless they have ops or voice in the channel.
7063 * 2: They're allowed to join through the ban (an invite in
7064 * ircu2.10, or a +e on Hybrid, or something).
7065 * If they're not joining through a ban, and the banlist is not
7066 * full, see if they're on the banlist for the channel. If so,
7069 if(user
->uplink
->burst
&& !mNode
->modes
)
7072 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7074 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7076 /* Riding a netburst. Naughty. */
7077 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7084 if(user
->handle_info
)
7086 handle
= user
->handle_info
;
7089 uData
= GetTrueChannelAccess(cData
, handle
);
7095 mod_chanmode_init(&change
);
7098 /* TODO: maybe only people above inviteme level? -Rubin */
7099 /* We don't kick people with access */
7102 if(channel
->banlist
.used
< MAXBANS
)
7104 /* Not joining through a ban. */
7105 for(bData
= cData
->bans
;
7106 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7107 bData
= bData
->next
);
7111 char kick_reason
[MAXLEN
];
7112 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7114 bData
->triggered
= now
;
7115 if(bData
!= cData
->bans
)
7117 /* Shuffle the ban to the head of the list. */
7119 bData
->next
->prev
= bData
->prev
;
7121 bData
->prev
->next
= bData
->next
;
7124 bData
->next
= cData
->bans
;
7127 cData
->bans
->prev
= bData
;
7128 cData
->bans
= bData
;
7131 change
.args
[0].mode
= MODE_BAN
;
7132 change
.args
[0].u
.hostmask
= bData
->mask
;
7133 mod_chanmode_announce(chanserv
, channel
, &change
);
7134 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7140 /* ChanServ will not modify the limits in join-flooded channels.
7141 It will also skip DynLimit processing when the user (or srvx)
7142 is bursting in, because there are likely more incoming. */
7143 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7144 && !user
->uplink
->burst
7145 && !channel
->join_flooded
7146 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7148 /* The user count has begun "bumping" into the channel limit,
7149 so set a timer to raise the limit a bit. Any previous
7150 timers are removed so three incoming users within the delay
7151 results in one limit change, not three. */
7153 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7154 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7157 /* Give automodes exept during join-floods */
7158 if(!channel
->join_flooded
)
7160 if(cData
->chOpts
[chAutomode
] == 'v')
7161 modes
|= MODE_VOICE
;
7162 else if(cData
->chOpts
[chAutomode
] == 'h')
7163 modes
|= MODE_HALFOP
;
7164 else if(cData
->chOpts
[chAutomode
] == 'o')
7165 modes
|= MODE_CHANOP
;
7168 greeting
= cData
->greeting
;
7169 if(user
->handle_info
)
7171 /* handle = user->handle_info; */
7173 if(IsHelper(user
) && !IsHelping(user
))
7176 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7178 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7180 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7186 /* uData = GetTrueChannelAccess(cData, handle); */
7187 if(uData
&& !IsUserSuspended(uData
))
7189 /* non users getting automodes are handled above. */
7190 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7192 /* just op everyone with access */
7193 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7194 modes
|= MODE_VOICE
;
7195 /* or do their access level */
7196 else if(uData
->access
>= UL_OP
)
7197 modes
|= MODE_CHANOP
;
7198 else if(uData
->access
>= UL_HALFOP
)
7199 modes
|= MODE_HALFOP
;
7200 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7201 modes
|= MODE_VOICE
;
7203 if(uData
->access
>= UL_PRESENT
)
7204 cData
->visited
= now
;
7205 if(cData
->user_greeting
)
7206 greeting
= cData
->user_greeting
;
7208 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7209 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7217 /* If user joining normally (not during burst), apply op or voice,
7218 * and send greeting/userinfo as appropriate.
7220 if(!user
->uplink
->burst
)
7224 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7225 if(modes & MODE_CHANOP) {
7226 modes &= ~MODE_HALFOP;
7227 modes &= ~MODE_VOICE;
7230 change
.args
[0].mode
= modes
;
7231 change
.args
[0].u
.member
= mNode
;
7232 mod_chanmode_announce(chanserv
, channel
, &change
);
7235 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7237 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7243 chanserv_autojoin_channels(struct userNode
*user
)
7245 struct userData
*channel
;
7247 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7249 struct chanNode
*cn
;
7250 struct modeNode
*mn
;
7252 if(IsUserSuspended(channel
)
7253 || IsSuspended(channel
->channel
)
7254 || !(cn
= channel
->channel
->channel
))
7257 mn
= GetUserMode(cn
, user
);
7260 if(!IsUserSuspended(channel
)
7261 && IsUserAutoJoin(channel
)
7262 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7264 && !user
->uplink
->burst
)
7265 irc_svsjoin(chanserv
, user
, cn
);
7271 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7273 struct mod_chanmode change
;
7274 struct userData
*channel
;
7275 unsigned int ii
, jj
, i
;
7277 if(!user
->handle_info
)
7280 mod_chanmode_init(&change
);
7282 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7284 struct chanNode
*cn
;
7285 struct chanData
*cData
;
7286 struct modeNode
*mn
;
7287 if(IsUserSuspended(channel
)
7288 || IsSuspended(channel
->channel
)
7289 || !(cn
= channel
->channel
->channel
))
7292 cData
= cn
->channel_info
;
7293 mn
= GetUserMode(cn
, user
);
7296 if(!IsUserSuspended(channel
)
7297 && IsUserAutoInvite(channel
)
7298 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7300 && !user
->uplink
->burst
)
7301 irc_invite(chanserv
, user
, cn
);
7305 if(channel
->access
>= UL_PRESENT
)
7306 channel
->channel
->visited
= now
;
7308 if(IsUserAutoOp(channel
) && cData
->chOpts
[chAutomode
] != 'n')
7310 if(channel
->access
>= UL_OP
)
7311 change
.args
[0].mode
= MODE_CHANOP
;
7312 else if(channel
->access
>= UL_HALFOP
)
7313 change
.args
[0].mode
= MODE_HALFOP
;
7314 else if(channel
->access
>= UL_PEON
)
7315 change
.args
[0].mode
= MODE_VOICE
;
7317 change
.args
[0].mode
= 0;
7318 change
.args
[0].u
.member
= mn
;
7319 if(change
.args
[0].mode
)
7320 mod_chanmode_announce(chanserv
, cn
, &change
);
7323 channel
->seen
= now
;
7324 channel
->present
= 1;
7327 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7329 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7330 struct banData
*ban
;
7332 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7333 || !channel
->channel_info
7334 || IsSuspended(channel
->channel_info
))
7336 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7338 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7339 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7341 if(jj
< channel
->banlist
.used
)
7343 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7345 char kick_reason
[MAXLEN
];
7346 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7348 change
.args
[0].mode
= MODE_BAN
;
7349 change
.args
[0].u
.hostmask
= ban
->mask
;
7350 mod_chanmode_announce(chanserv
, channel
, &change
);
7351 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7352 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7353 ban
->triggered
= now
;
7358 if(IsSupportHelper(user
))
7360 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7362 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7364 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7370 if (user
->handle_info
->ignores
->used
) {
7371 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7372 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7376 if (user
->handle_info
->epithet
)
7377 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7379 /* process autojoin channels 5 seconds later as this sometimes
7380 happens before autohide */
7381 // timeq_add(now + 5, chanserv_autojoin_channels, user);
7382 chanserv_autojoin_channels(user
);
7386 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7388 struct chanData
*cData
;
7389 struct userData
*uData
;
7391 cData
= mn
->channel
->channel_info
;
7392 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7395 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7397 /* Allow for a bit of padding so that the limit doesn't
7398 track the user count exactly, which could get annoying. */
7399 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7401 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7402 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7406 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7408 scan_user_presence(uData
, mn
->user
);
7412 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7414 unsigned int ii
, jj
;
7415 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7417 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7418 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7420 if(jj
< mn
->user
->channels
.used
)
7423 if(ii
== chanserv_conf
.support_channels
.used
)
7424 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7429 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7431 struct userData
*uData
;
7433 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7434 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7435 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7438 if(protect_user(victim
, kicker
, channel
->channel_info
, false))
7440 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7441 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7444 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7449 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7451 struct chanData
*cData
;
7453 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7456 cData
= channel
->channel_info
;
7457 if(bad_topic(channel
, user
, channel
->topic
))
7458 { /* User doesnt have privs to set topics. Undo it */
7459 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7460 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7463 /* If there is a topic mask set, and the new topic doesnt match,
7464 * set the topic to mask + new_topic */
7465 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7467 char new_topic
[TOPICLEN
+1];
7468 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7471 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7472 /* and fall through to topicsnarf code below.. */
7474 else /* Topic couldnt fit into mask, was too long */
7476 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7477 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7478 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7482 /* With topicsnarf, grab the topic and save it as the default topic. */
7483 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7486 cData
->topic
= strdup(channel
->topic
);
7492 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7494 struct mod_chanmode
*bounce
= NULL
;
7495 unsigned int bnc
, ii
;
7498 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7501 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7502 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7504 char correct
[MAXLEN
];
7505 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7506 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7507 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7509 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7511 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7513 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7514 if(!protect_user(victim
, user
, channel
->channel_info
, false))
7517 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7520 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7521 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7522 if(bounce
->args
[bnc
].u
.member
)
7526 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7527 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7529 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7531 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7533 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7534 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7537 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7538 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7539 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7542 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7544 const char *ban
= change
->args
[ii
].u
.hostmask
;
7545 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7548 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7549 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7550 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7552 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7557 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7558 mod_chanmode_announce(chanserv
, channel
, bounce
);
7559 for(ii
= 0; ii
< change
->argc
; ++ii
)
7560 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7561 free((char*)bounce
->args
[ii
].u
.hostmask
);
7562 mod_chanmode_free(bounce
);
7567 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7569 struct chanNode
*channel
;
7570 struct banData
*bData
;
7571 struct mod_chanmode change
;
7572 unsigned int ii
, jj
;
7573 char kick_reason
[MAXLEN
];
7575 mod_chanmode_init(&change
);
7577 change
.args
[0].mode
= MODE_BAN
;
7578 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7580 channel
= user
->channels
.list
[ii
]->channel
;
7581 /* Need not check for bans if they're opped or voiced. */
7582 /* TODO: does this make sense in automode v, h, and o? *
7583 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7584 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7586 /* Need not check for bans unless channel registration is active. */
7587 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7589 /* Look for a matching ban already on the channel. */
7590 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7591 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7593 /* Need not act if we found one. */
7594 if(jj
< channel
->banlist
.used
)
7596 /* don't kick someone on the userlist */
7597 if(protect_user(user
, chanserv
, channel
->channel_info
, true))
7599 /* Look for a matching ban in this channel. */
7600 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7602 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7604 change
.args
[0].u
.hostmask
= bData
->mask
;
7605 mod_chanmode_announce(chanserv
, channel
, &change
);
7606 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7607 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7608 bData
->triggered
= now
;
7609 break; /* we don't need to check any more bans in the channel */
7614 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7616 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7620 dict_remove2(handle_dnrs
, old_handle
, 1);
7621 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7622 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7627 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7629 struct userNode
*h_user
;
7631 if(handle
->channels
)
7633 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7634 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7636 while(handle
->channels
)
7637 del_channel_user(handle
->channels
, 1);
7642 handle_server_link(UNUSED_ARG(struct server
*server
))
7644 struct chanData
*cData
;
7646 for(cData
= channelList
; cData
; cData
= cData
->next
)
7648 if(!IsSuspended(cData
))
7649 cData
->may_opchan
= 1;
7650 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7651 && !cData
->channel
->join_flooded
7652 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7653 < chanserv_conf
.adjust_threshold
))
7655 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7656 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7662 chanserv_conf_read(void)
7666 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7667 struct mod_chanmode
*change
;
7668 struct string_list
*strlist
;
7669 struct chanNode
*chan
;
7672 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7674 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7677 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7678 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7679 chanserv_conf
.support_channels
.used
= 0;
7680 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7682 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7684 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7687 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7689 channelList_append(&chanserv_conf
.support_channels
, chan
);
7692 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7695 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7698 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7700 channelList_append(&chanserv_conf
.support_channels
, chan
);
7702 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7703 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7704 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7705 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7706 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7707 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7708 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7709 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7710 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7711 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7712 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7713 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7714 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7715 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7716 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7717 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7718 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7719 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7720 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7721 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7722 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7723 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7724 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7725 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7726 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7728 NickChange(chanserv
, str
, 0);
7729 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7730 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7731 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7732 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7733 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7734 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7735 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7736 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7737 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7738 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7739 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7740 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7741 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7742 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7743 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7744 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7745 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7746 god_timeout
= str
? ParseInterval(str
) : 60*15;
7747 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7750 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7751 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7752 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7753 && (change
->argc
< 2))
7755 chanserv_conf
.default_modes
= *change
;
7756 mod_chanmode_free(change
);
7758 free_string_list(chanserv_conf
.set_shows
);
7759 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7761 strlist
= string_list_copy(strlist
);
7764 static const char *list
[] = {
7765 /* free form text */
7766 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7767 /* options based on user level */
7768 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7769 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7770 /* multiple choice options */
7771 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7772 /* binary options */
7773 "DynLimit", "NoDelete", "BanTimeout",
7778 strlist
= alloc_string_list(ArrayLength(list
)-1);
7779 for(ii
=0; list
[ii
]; ii
++)
7780 string_list_append(strlist
, strdup(list
[ii
]));
7782 chanserv_conf
.set_shows
= strlist
;
7783 /* We don't look things up now, in case the list refers to options
7784 * defined by modules initialized after this point. Just mark the
7785 * function list as invalid, so it will be initialized.
7787 set_shows_list
.used
= 0;
7788 free_string_list(chanserv_conf
.eightball
);
7789 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7792 strlist
= string_list_copy(strlist
);
7796 strlist
= alloc_string_list(4);
7797 string_list_append(strlist
, strdup("Yes."));
7798 string_list_append(strlist
, strdup("No."));
7799 string_list_append(strlist
, strdup("Maybe so."));
7801 chanserv_conf
.eightball
= strlist
;
7802 free_string_list(chanserv_conf
.old_ban_names
);
7803 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7805 strlist
= string_list_copy(strlist
);
7807 strlist
= alloc_string_list(2);
7808 chanserv_conf
.old_ban_names
= strlist
;
7809 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7810 off_channel
= str
? atoi(str
) : 0;
7814 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7817 struct note_type
*ntype
;
7820 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7822 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7825 if(!(ntype
= chanserv_create_note_type(key
)))
7827 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7831 /* Figure out set access */
7832 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7834 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7835 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7837 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7839 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7840 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7842 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7844 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7848 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7849 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7850 ntype
->set_access
.min_opserv
= 0;
7853 /* Figure out visibility */
7854 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7855 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7856 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7857 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7858 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7859 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7860 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7861 ntype
->visible_type
= NOTE_VIS_ALL
;
7863 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7865 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7866 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7870 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7872 struct handle_info
*handle
;
7873 struct userData
*uData
;
7874 char *seen
, *inf
, *flags
, *expires
;
7876 unsigned short access
;
7878 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7880 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7884 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7885 if(access
> UL_OWNER
)
7887 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7891 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7892 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7893 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7894 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7895 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7896 handle
= get_handle_info(key
);
7899 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7903 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7904 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7905 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7907 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7909 if(uData
->expires
> now
)
7910 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7912 uData
->flags
&= ~USER_SUSPENDED
;
7915 /* Upgrade: set autoop to the inverse of noautoop */
7916 if(chanserv_read_version
< 2)
7918 /* if noautoop is true, set autoop false, and vice versa */
7919 if(uData
->flags
& USER_NOAUTO_OP
)
7920 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7922 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7923 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
);
7929 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7931 struct banData
*bData
;
7932 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7933 time_t set_time
, triggered_time
, expires_time
;
7935 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7937 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7941 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7942 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7943 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7944 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7945 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7946 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7947 if (!reason
|| !owner
)
7950 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7951 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7953 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7955 expires_time
= set_time
+ atoi(s_duration
);
7959 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7962 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7965 static struct suspended
*
7966 chanserv_read_suspended(dict_t obj
)
7968 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7972 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7973 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7974 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7975 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7976 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7977 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7978 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7979 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7980 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7981 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7985 static struct giveownership
*
7986 chanserv_read_giveownership(dict_t obj
)
7988 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7992 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7993 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7995 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7997 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7998 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
8000 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
8001 giveownership
->reason
= str
? strdup(str
) : NULL
;
8002 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
8003 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8005 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
8006 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
8007 return giveownership
;
8011 chanserv_channel_read(const char *key
, struct record_data
*hir
)
8013 struct suspended
*suspended
;
8014 struct giveownership
*giveownership
;
8015 struct mod_chanmode
*modes
;
8016 struct chanNode
*cNode
;
8017 struct chanData
*cData
;
8018 struct dict
*channel
, *obj
;
8019 char *str
, *argv
[10];
8023 channel
= hir
->d
.object
;
8025 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
8028 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
8031 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
8034 cData
= register_channel(cNode
, str
);
8037 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
8041 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
8043 enum levelOption lvlOpt
;
8044 enum charOption chOpt
;
8046 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
8047 cData
->flags
= atoi(str
);
8049 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8051 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
8053 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
8054 else if(levelOptions
[lvlOpt
].old_flag
)
8056 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8057 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
8059 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
8063 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8065 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
8067 cData
->chOpts
[chOpt
] = str
[0];
8070 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
8072 enum levelOption lvlOpt
;
8073 enum charOption chOpt
;
8076 cData
->flags
= base64toint(str
, 5);
8077 count
= strlen(str
+= 5);
8078 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8081 if(levelOptions
[lvlOpt
].old_flag
)
8083 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8084 lvl
= levelOptions
[lvlOpt
].flag_value
;
8086 lvl
= levelOptions
[lvlOpt
].default_value
;
8088 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8090 case 'c': lvl
= UL_COOWNER
; break;
8091 case 'm': lvl
= UL_MANAGER
; break;
8092 case 'n': lvl
= UL_OWNER
+1; break;
8093 case 'o': lvl
= UL_OP
; break;
8094 case 'p': lvl
= UL_PEON
; break;
8095 case 'h': lvl
= UL_HALFOP
; break;
8096 case 'w': lvl
= UL_OWNER
; break;
8097 default: lvl
= 0; break;
8099 cData
->lvlOpts
[lvlOpt
] = lvl
;
8101 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8102 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8105 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8107 suspended
= chanserv_read_suspended(obj
);
8108 cData
->suspended
= suspended
;
8109 suspended
->cData
= cData
;
8110 /* We could use suspended->expires and suspended->revoked to
8111 * set the CHANNEL_SUSPENDED flag, but we don't. */
8113 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8115 suspended
= calloc(1, sizeof(*suspended
));
8116 suspended
->issued
= 0;
8117 suspended
->revoked
= 0;
8118 suspended
->suspender
= strdup(str
);
8119 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8120 suspended
->expires
= str
? atoi(str
) : 0;
8121 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8122 suspended
->reason
= strdup(str
? str
: "No reason");
8123 suspended
->previous
= NULL
;
8124 cData
->suspended
= suspended
;
8125 suspended
->cData
= cData
;
8129 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8130 suspended
= NULL
; /* to squelch a warning */
8133 if(IsSuspended(cData
)) {
8134 if(suspended
->expires
> now
)
8135 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8136 else if(suspended
->expires
)
8137 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8140 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8142 giveownership
= chanserv_read_giveownership(obj
);
8143 cData
->giveownership
= giveownership
;
8146 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8147 struct mod_chanmode change
;
8148 mod_chanmode_init(&change
);
8150 change
.args
[0].mode
= MODE_CHANOP
;
8151 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8152 mod_chanmode_announce(chanserv
, cNode
, &change
);
8155 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8156 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8157 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8158 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8159 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8160 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8161 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8162 cData
->max
= str
? atoi(str
) : 0;
8163 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8164 cData
->greeting
= str
? strdup(str
) : NULL
;
8165 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8166 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8167 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8168 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8169 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8170 cData
->topic
= str
? strdup(str
) : NULL
;
8172 if(!IsSuspended(cData
)
8173 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8174 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8175 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8176 cData
->modes
= *modes
;
8178 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8179 if(cData
->modes
.argc
> 1)
8180 cData
->modes
.argc
= 1;
8181 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8182 mod_chanmode_free(modes
);
8185 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8186 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8187 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8189 if(!cData
->users
&& !IsProtected(cData
))
8191 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8192 unregister_channel(cData
, "has empty user list.");
8196 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8197 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8198 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8200 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8201 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8203 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8204 struct record_data
*rd
= iter_data(it
);
8205 const char *note
, *setter
;
8207 if(rd
->type
!= RECDB_OBJECT
)
8209 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8213 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8215 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8217 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8221 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8222 if(!setter
) setter
= "<unknown>";
8223 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8231 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8233 const char *setter
, *reason
, *str
;
8234 struct do_not_register
*dnr
;
8236 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8239 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8242 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8245 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8248 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8251 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8253 dnr
->set
= atoi(str
);
8259 chanserv_version_read(struct dict
*section
)
8263 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8265 chanserv_read_version
= atoi(str
);
8266 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8270 chanserv_saxdb_read(struct dict
*database
)
8272 struct dict
*section
;
8275 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8276 chanserv_version_read(section
);
8278 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8279 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8280 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8282 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8283 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8284 chanserv_channel_read(iter_key(it
), iter_data(it
));
8286 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8287 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8288 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8294 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8296 int high_present
= 0;
8297 saxdb_start_record(ctx
, KEY_USERS
, 1);
8298 for(; uData
; uData
= uData
->next
)
8300 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8302 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8303 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8304 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8306 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8308 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8310 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8311 saxdb_end_record(ctx
);
8313 saxdb_end_record(ctx
);
8314 return high_present
;
8318 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8322 saxdb_start_record(ctx
, KEY_BANS
, 1);
8323 for(; bData
; bData
= bData
->next
)
8325 saxdb_start_record(ctx
, bData
->mask
, 0);
8326 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8327 if(bData
->triggered
)
8328 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8330 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8332 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8334 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8335 saxdb_end_record(ctx
);
8337 saxdb_end_record(ctx
);
8341 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8343 saxdb_start_record(ctx
, name
, 0);
8344 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8345 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8347 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8349 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8351 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8353 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8354 saxdb_end_record(ctx
);
8358 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8360 saxdb_start_record(ctx
, name
, 0);
8361 if(giveownership
->staff_issuer
)
8362 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8363 if(giveownership
->old_owner
)
8364 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8365 if(giveownership
->target
)
8366 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8367 if(giveownership
->target_access
)
8368 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8369 if(giveownership
->reason
)
8370 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8371 if(giveownership
->issued
)
8372 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8373 if(giveownership
->previous
)
8374 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8375 saxdb_end_record(ctx
);
8379 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8383 enum levelOption lvlOpt
;
8384 enum charOption chOpt
;
8386 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8388 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8389 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8391 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8392 if(channel
->registrar
)
8393 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8394 if(channel
->greeting
)
8395 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8396 if(channel
->user_greeting
)
8397 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8398 if(channel
->topic_mask
)
8399 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8400 if(channel
->suspended
)
8401 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8402 if(channel
->giveownership
)
8403 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8405 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8406 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8407 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8408 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8409 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8411 buf
[0] = channel
->chOpts
[chOpt
];
8413 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8415 saxdb_end_record(ctx
);
8417 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8419 mod_chanmode_format(&channel
->modes
, buf
);
8420 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8423 high_present
= chanserv_write_users(ctx
, channel
->users
);
8424 chanserv_write_bans(ctx
, channel
->bans
);
8426 if(dict_size(channel
->notes
))
8430 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8431 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8433 struct note
*note
= iter_data(it
);
8434 saxdb_start_record(ctx
, iter_key(it
), 0);
8435 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8436 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8437 saxdb_end_record(ctx
);
8439 saxdb_end_record(ctx
);
8442 if(channel
->ownerTransfer
)
8443 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8444 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8445 saxdb_end_record(ctx
);
8449 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8453 saxdb_start_record(ctx
, ntype
->name
, 0);
8454 switch(ntype
->set_access_type
)
8456 case NOTE_SET_CHANNEL_ACCESS
:
8457 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8459 case NOTE_SET_CHANNEL_SETTER
:
8460 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8462 case NOTE_SET_PRIVILEGED
: default:
8463 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8466 switch(ntype
->visible_type
)
8468 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8469 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8470 case NOTE_VIS_PRIVILEGED
:
8471 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8473 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8474 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8475 saxdb_end_record(ctx
);
8479 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8481 struct do_not_register
*dnr
;
8484 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8486 dnr
= iter_data(it
);
8487 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8489 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8490 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8491 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8492 saxdb_end_record(ctx
);
8497 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8500 struct chanData
*channel
;
8502 /* Version Control*/
8503 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8504 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8505 saxdb_end_record(ctx
);
8508 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8509 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8510 chanserv_write_note_type(ctx
, iter_data(it
));
8511 saxdb_end_record(ctx
);
8514 saxdb_start_record(ctx
, KEY_DNR
, 1);
8515 write_dnrs_helper(ctx
, handle_dnrs
);
8516 write_dnrs_helper(ctx
, plain_dnrs
);
8517 write_dnrs_helper(ctx
, mask_dnrs
);
8518 saxdb_end_record(ctx
);
8521 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8522 for(channel
= channelList
; channel
; channel
= channel
->next
)
8523 chanserv_write_channel(ctx
, channel
);
8524 saxdb_end_record(ctx
);
8530 chanserv_db_cleanup(void) {
8532 unreg_part_func(handle_part
);
8534 unregister_channel(channelList
, "terminating.");
8535 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8536 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8537 free(chanserv_conf
.support_channels
.list
);
8538 dict_delete(handle_dnrs
);
8539 dict_delete(plain_dnrs
);
8540 dict_delete(mask_dnrs
);
8541 dict_delete(note_types
);
8542 free_string_list(chanserv_conf
.eightball
);
8543 free_string_list(chanserv_conf
.old_ban_names
);
8544 free_string_list(chanserv_conf
.set_shows
);
8545 free(set_shows_list
.list
);
8546 free(uset_shows_list
.list
);
8549 struct userData
*helper
= helperList
;
8550 helperList
= helperList
->next
;
8555 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8556 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8557 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8560 init_chanserv(const char *nick
)
8562 struct chanNode
*chan
;
8564 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8565 conf_register_reload(chanserv_conf_read
);
8567 reg_server_link_func(handle_server_link
);
8569 reg_new_channel_func(handle_new_channel
);
8570 reg_join_func(handle_join
);
8571 reg_part_func(handle_part
);
8572 reg_kick_func(handle_kick
);
8573 reg_topic_func(handle_topic
);
8574 reg_mode_change_func(handle_mode
);
8575 reg_nick_change_func(handle_nick_change
);
8577 reg_auth_func(handle_auth
);
8578 reg_handle_rename_func(handle_rename
);
8579 reg_unreg_func(handle_unreg
);
8581 handle_dnrs
= dict_new();
8582 dict_set_free_data(handle_dnrs
, free
);
8583 plain_dnrs
= dict_new();
8584 dict_set_free_data(plain_dnrs
, free
);
8585 mask_dnrs
= dict_new();
8586 dict_set_free_data(mask_dnrs
, free
);
8588 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8589 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8590 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8591 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8592 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8593 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8594 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8595 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8596 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8597 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8599 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8601 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8602 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8604 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8605 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8606 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8607 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8608 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8610 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8611 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8612 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8613 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8614 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8615 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8617 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8618 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8619 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8620 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8622 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8623 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8624 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8625 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8626 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8627 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8628 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8629 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8630 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8631 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8633 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8634 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8635 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8636 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8637 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8638 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8639 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8640 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8641 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8642 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8643 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8644 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8645 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8646 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8648 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8649 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8650 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8651 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8653 /* if you change dellamer access, see also places
8654 * like unbanme which have manager hardcoded. */
8655 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8656 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8658 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8660 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8662 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8663 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8664 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8665 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8666 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8667 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8668 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8669 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8670 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8671 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8672 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8673 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8675 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8676 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8678 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8679 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8680 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8681 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8683 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8684 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8685 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8686 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8687 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8689 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8690 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8691 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8692 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8693 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8694 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8695 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8696 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8698 /* Channel options */
8699 DEFINE_CHANNEL_OPTION(defaulttopic
);
8700 DEFINE_CHANNEL_OPTION(topicmask
);
8701 DEFINE_CHANNEL_OPTION(greeting
);
8702 DEFINE_CHANNEL_OPTION(usergreeting
);
8703 DEFINE_CHANNEL_OPTION(modes
);
8704 DEFINE_CHANNEL_OPTION(enfops
);
8705 DEFINE_CHANNEL_OPTION(enfhalfops
);
8706 DEFINE_CHANNEL_OPTION(automode
);
8707 DEFINE_CHANNEL_OPTION(protect
);
8708 DEFINE_CHANNEL_OPTION(enfmodes
);
8709 DEFINE_CHANNEL_OPTION(enftopic
);
8710 DEFINE_CHANNEL_OPTION(pubcmd
);
8711 DEFINE_CHANNEL_OPTION(userinfo
);
8712 DEFINE_CHANNEL_OPTION(dynlimit
);
8713 DEFINE_CHANNEL_OPTION(topicsnarf
);
8714 DEFINE_CHANNEL_OPTION(nodelete
);
8715 DEFINE_CHANNEL_OPTION(toys
);
8716 DEFINE_CHANNEL_OPTION(setters
);
8717 DEFINE_CHANNEL_OPTION(topicrefresh
);
8718 DEFINE_CHANNEL_OPTION(resync
);
8719 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8720 DEFINE_CHANNEL_OPTION(bantimeout
);
8721 DEFINE_CHANNEL_OPTION(inviteme
);
8723 DEFINE_CHANNEL_OPTION(offchannel
);
8724 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8726 /* Alias set topic to set defaulttopic for compatibility. */
8727 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8730 DEFINE_USER_OPTION(autoinvite
);
8731 DEFINE_USER_OPTION(autojoin
);
8732 DEFINE_USER_OPTION(info
);
8733 DEFINE_USER_OPTION(autoop
);
8735 /* Alias uset autovoice to uset autoop. */
8736 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8738 note_types
= dict_new();
8739 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8742 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8743 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8744 service_register(chanserv
)->trigger
= '!';
8745 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8748 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8750 if(chanserv_conf
.channel_expire_frequency
)
8751 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8753 if(chanserv_conf
.ban_timeout_frequency
)
8754 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8756 if(chanserv_conf
.refresh_period
)
8758 time_t next_refresh
;
8759 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8760 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8761 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8764 if (autojoin_channels
&& chanserv
) {
8765 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8766 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8767 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8771 reg_exit_func(chanserv_db_cleanup
);
8772 message_register_table(msgtab
);