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
)
1643 char protect
= channel
->chOpts
[chProtect
];
1644 struct userData
*cs_victim
, *cs_aggressor
;
1646 /* Don't protect if no one is to be protected, someone is attacking
1647 himself, or if the aggressor is an IRC Operator. */
1648 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1651 /* Don't protect if the victim isn't authenticated (because they
1652 can't be a channel user), unless we are to protect non-users
1654 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1655 if(protect
!= 'a' && !cs_victim
)
1658 /* Protect if the aggressor isn't a user because at this point,
1659 the aggressor can only be less than or equal to the victim. */
1660 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1664 /* If the aggressor was a user, then the victim can't be helped. */
1671 if(cs_victim
->access
> cs_aggressor
->access
)
1676 if(cs_victim
->access
>= cs_aggressor
->access
)
1685 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1687 struct chanData
*cData
= channel
->channel_info
;
1688 struct userData
*cs_victim
;
1690 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1691 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1692 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1695 reply("CSMSG_OPBY_LOCKED");
1697 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1705 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1707 struct chanData
*cData
= channel
->channel_info
;
1708 struct userData
*cs_victim
;
1710 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1711 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1712 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1714 reply("CSMSG_HOPBY_LOCKED");
1723 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1725 if(IsService(victim
))
1727 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1731 if(protect_user(victim
, user
, channel
->channel_info
))
1733 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1741 validate_dehop(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
))
1751 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1758 static struct do_not_register
*
1759 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1761 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1762 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1763 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1764 strcpy(dnr
->reason
, reason
);
1766 if(dnr
->chan_name
[0] == '*')
1767 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1768 else if(strpbrk(dnr
->chan_name
, "*?"))
1769 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1771 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1775 static struct dnrList
1776 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1778 struct dnrList list
;
1780 struct do_not_register
*dnr
;
1782 dnrList_init(&list
);
1783 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1784 dnrList_append(&list
, dnr
);
1785 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1786 dnrList_append(&list
, dnr
);
1788 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1789 if(match_ircglob(chan_name
, iter_key(it
)))
1790 dnrList_append(&list
, iter_data(it
));
1795 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1797 struct dnrList list
;
1798 struct do_not_register
*dnr
;
1800 char buf
[INTERVALLEN
];
1802 list
= chanserv_find_dnrs(chan_name
, handle
);
1803 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1805 dnr
= list
.list
[ii
];
1808 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1809 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1812 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1815 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1820 struct do_not_register
*
1821 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1823 struct do_not_register
*dnr
;
1826 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1830 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1832 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1833 if(match_ircglob(chan_name
, iter_key(it
)))
1834 return iter_data(it
);
1839 static CHANSERV_FUNC(cmd_noregister
)
1842 struct do_not_register
*dnr
;
1843 char buf
[INTERVALLEN
];
1844 unsigned int matches
;
1850 reply("CSMSG_DNR_SEARCH_RESULTS");
1851 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1854 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1856 dnr
= iter_data(it
);
1858 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1860 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1863 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1865 dnr
= iter_data(it
);
1867 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1869 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1872 for(it
= dict_first(mask_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
);
1883 reply("MSG_MATCH_COUNT", matches
);
1885 reply("MSG_NO_MATCHES");
1891 if(!IsChannelName(target
) && (*target
!= '*'))
1893 reply("CSMSG_NOT_DNR", target
);
1899 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1900 if((*target
== '*') && !get_handle_info(target
+ 1))
1902 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1905 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1906 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1910 reply("CSMSG_DNR_SEARCH_RESULTS");
1911 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1914 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1916 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1918 reply("MSG_NO_MATCHES");
1922 static CHANSERV_FUNC(cmd_allowregister
)
1924 const char *chan_name
= argv
[1];
1926 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1928 dict_remove(handle_dnrs
, chan_name
+1);
1929 reply("CSMSG_DNR_REMOVED", chan_name
);
1931 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1933 dict_remove(plain_dnrs
, chan_name
);
1934 reply("CSMSG_DNR_REMOVED", chan_name
);
1936 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1938 dict_remove(mask_dnrs
, chan_name
);
1939 reply("CSMSG_DNR_REMOVED", chan_name
);
1943 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1950 chanserv_get_owned_count(struct handle_info
*hi
)
1952 struct userData
*cList
;
1955 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1956 if(cList
->access
== UL_OWNER
)
1961 static CHANSERV_FUNC(cmd_register
)
1963 struct handle_info
*handle
;
1964 struct chanData
*cData
;
1965 struct modeNode
*mn
;
1966 char reason
[MAXLEN
];
1968 unsigned int new_channel
, force
=0;
1969 struct do_not_register
*dnr
;
1972 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
1973 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
1979 if(channel
->channel_info
)
1981 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1985 if(channel
->bad_channel
)
1987 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1991 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1993 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1998 chan_name
= channel
->name
;
2004 reply("MSG_MISSING_PARAMS", cmd
->name
);
2005 svccmd_send_help_brief(user
, chanserv
, cmd
);
2008 if(!IsChannelName(argv
[1]))
2010 reply("MSG_NOT_CHANNEL_NAME");
2014 if(opserv_bad_channel(argv
[1]))
2016 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2021 chan_name
= argv
[1];
2024 if(argc
>= (new_channel
+2))
2026 if(!IsHelping(user
))
2028 reply("CSMSG_PROXY_FORBIDDEN");
2032 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2034 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2035 dnr
= chanserv_is_dnr(chan_name
, handle
);
2037 /* Check if they are over the limit.. */
2038 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2040 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2047 handle
= user
->handle_info
;
2048 dnr
= chanserv_is_dnr(chan_name
, handle
);
2049 /* Check if they are over the limit.. */
2050 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2052 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2055 /* Check if another service is in the channel */
2057 for(n
= 0; n
< channel
->members
.used
; n
++)
2059 mn
= channel
->members
.list
[n
];
2060 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2062 reply("CSMSG_ANOTHER_SERVICE");
2069 if(!IsHelping(user
))
2070 reply("CSMSG_DNR_CHANNEL", chan_name
);
2072 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2076 /* now handled above for message specilization *
2077 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2079 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2085 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2087 cData
= register_channel(channel
, user
->handle_info
->handle
);
2088 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2089 cData
->modes
= chanserv_conf
.default_modes
;
2091 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2092 if (IsOffChannel(cData
))
2094 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2098 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2099 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2100 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2102 mod_chanmode_announce(chanserv
, channel
, change
);
2103 mod_chanmode_free(change
);
2106 /* Initialize the channel's max user record. */
2107 cData
->max
= channel
->members
.used
;
2109 if(handle
!= user
->handle_info
)
2110 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2113 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2114 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2115 handle
->handle
, user
->handle_info
->handle
);
2120 make_confirmation_string(struct userData
*uData
)
2122 static char strbuf
[16];
2127 for(src
= uData
->handle
->handle
; *src
; )
2128 accum
= accum
* 31 + toupper(*src
++);
2130 for(src
= uData
->channel
->channel
->name
; *src
; )
2131 accum
= accum
* 31 + toupper(*src
++);
2132 sprintf(strbuf
, "%08x", accum
);
2136 static CHANSERV_FUNC(cmd_unregister
)
2139 char reason
[MAXLEN
];
2140 struct chanData
*cData
;
2141 struct userData
*uData
;
2143 cData
= channel
->channel_info
;
2146 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2150 uData
= GetChannelUser(cData
, user
->handle_info
);
2151 if(!uData
|| (uData
->access
< UL_OWNER
))
2153 reply("CSMSG_NO_ACCESS");
2157 if(IsProtected(cData
))
2159 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2163 if(!IsHelping(user
))
2165 const char *confirm_string
;
2166 if(IsSuspended(cData
))
2168 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2171 confirm_string
= make_confirmation_string(uData
);
2172 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2174 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2179 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2180 name
= strdup(channel
->name
);
2181 unregister_channel(cData
, reason
);
2182 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2183 reply("CSMSG_UNREG_SUCCESS", name
);
2189 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2191 extern struct userNode
*spamserv
;
2192 struct mod_chanmode
*change
;
2194 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2196 change
= mod_chanmode_alloc(2);
2198 change
->args
[0].mode
= MODE_CHANOP
;
2199 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2200 change
->args
[1].mode
= MODE_CHANOP
;
2201 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2205 change
= mod_chanmode_alloc(1);
2207 change
->args
[0].mode
= MODE_CHANOP
;
2208 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2211 mod_chanmode_announce(chanserv
, channel
, change
);
2212 mod_chanmode_free(change
);
2215 static CHANSERV_FUNC(cmd_move
)
2217 struct mod_chanmode change
;
2218 struct chanNode
*target
;
2219 struct modeNode
*mn
;
2220 struct userData
*uData
;
2221 struct do_not_register
*dnr
;
2222 int chanserv_join
= 0, spamserv_join
;
2226 if(IsProtected(channel
->channel_info
))
2228 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2232 if(!IsChannelName(argv
[1]))
2234 reply("MSG_NOT_CHANNEL_NAME");
2238 if(opserv_bad_channel(argv
[1]))
2240 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2244 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2246 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2248 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2250 if(!IsHelping(user
))
2251 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2253 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2259 mod_chanmode_init(&change
);
2260 if(!(target
= GetChannel(argv
[1])))
2262 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2263 if(!IsSuspended(channel
->channel_info
))
2266 else if(target
->channel_info
)
2268 reply("CSMSG_ALREADY_REGGED", target
->name
);
2271 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2272 && !IsHelping(user
))
2274 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2277 else if(!IsSuspended(channel
->channel_info
))
2282 /* Clear MODE_REGISTERED from old channel, add it to new. */
2284 change
.modes_clear
= MODE_REGISTERED
;
2285 mod_chanmode_announce(chanserv
, channel
, &change
);
2286 change
.modes_clear
= 0;
2287 change
.modes_set
= MODE_REGISTERED
;
2288 mod_chanmode_announce(chanserv
, target
, &change
);
2291 /* Move the channel_info to the target channel; it
2292 shouldn't be necessary to clear timeq callbacks
2293 for the old channel. */
2294 target
->channel_info
= channel
->channel_info
;
2295 target
->channel_info
->channel
= target
;
2296 channel
->channel_info
= NULL
;
2298 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2301 ss_cs_join_channel(target
, spamserv_join
);
2303 if(!IsSuspended(target
->channel_info
))
2305 char reason2
[MAXLEN
];
2306 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2307 DelChannelUser(chanserv
, channel
, reason2
, 0);
2310 UnlockChannel(channel
);
2311 LockChannel(target
);
2312 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2313 channel
->name
, target
->name
, user
->handle_info
->handle
);
2315 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2320 merge_users(struct chanData
*source
, struct chanData
*target
)
2322 struct userData
*suData
, *tuData
, *next
;
2328 /* Insert the source's users into the scratch area. */
2329 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2330 dict_insert(merge
, suData
->handle
->handle
, suData
);
2332 /* Iterate through the target's users, looking for
2333 users common to both channels. The lower access is
2334 removed from either the scratch area or target user
2336 for(tuData
= target
->users
; tuData
; tuData
= next
)
2338 struct userData
*choice
;
2340 next
= tuData
->next
;
2342 /* If a source user exists with the same handle as a target
2343 channel's user, resolve the conflict by removing one. */
2344 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2348 /* Pick the data we want to keep. */
2349 /* If the access is the same, use the later seen time. */
2350 if(suData
->access
== tuData
->access
)
2351 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2352 else /* Otherwise, keep the higher access level. */
2353 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2355 /* Remove the user that wasn't picked. */
2356 if(choice
== tuData
)
2358 dict_remove(merge
, suData
->handle
->handle
);
2359 del_channel_user(suData
, 0);
2362 del_channel_user(tuData
, 0);
2365 /* Move the remaining users to the target channel. */
2366 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2368 suData
= iter_data(it
);
2370 /* Insert the user into the target channel's linked list. */
2371 suData
->prev
= NULL
;
2372 suData
->next
= target
->users
;
2373 suData
->channel
= target
;
2376 target
->users
->prev
= suData
;
2377 target
->users
= suData
;
2379 /* Update the user counts for the target channel; the
2380 source counts are left alone. */
2381 target
->userCount
++;
2384 /* Possible to assert (source->users == NULL) here. */
2385 source
->users
= NULL
;
2390 merge_bans(struct chanData
*source
, struct chanData
*target
)
2392 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2394 /* Hold on to the original head of the target ban list
2395 to avoid comparing source bans with source bans. */
2396 tFront
= target
->bans
;
2398 /* Perform a totally expensive O(n*m) merge, ick. */
2399 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2401 /* Flag to track whether the ban's been moved
2402 to the destination yet. */
2405 /* Possible to assert (sbData->prev == NULL) here. */
2406 sNext
= sbData
->next
;
2408 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2410 tNext
= tbData
->next
;
2412 /* Perform two comparisons between each source
2413 and target ban, conflicts are resolved by
2414 keeping the broader ban and copying the later
2415 expiration and triggered time. */
2416 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2418 /* There is a broader ban in the target channel that
2419 overrides one in the source channel; remove the
2420 source ban and break. */
2421 if(sbData
->expires
> tbData
->expires
)
2422 tbData
->expires
= sbData
->expires
;
2423 if(sbData
->triggered
> tbData
->triggered
)
2424 tbData
->triggered
= sbData
->triggered
;
2425 del_channel_ban(sbData
);
2428 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2430 /* There is a broader ban in the source channel that
2431 overrides one in the target channel; remove the
2432 target ban, fall through and move the source over. */
2433 if(tbData
->expires
> sbData
->expires
)
2434 sbData
->expires
= tbData
->expires
;
2435 if(tbData
->triggered
> sbData
->triggered
)
2436 sbData
->triggered
= tbData
->triggered
;
2437 if(tbData
== tFront
)
2439 del_channel_ban(tbData
);
2442 /* Source bans can override multiple target bans, so
2443 we allow a source to run through this loop multiple
2444 times, but we can only move it once. */
2449 /* Remove the source ban from the source ban list. */
2451 sbData
->next
->prev
= sbData
->prev
;
2453 /* Modify the source ban's associated channel. */
2454 sbData
->channel
= target
;
2456 /* Insert the ban into the target channel's linked list. */
2457 sbData
->prev
= NULL
;
2458 sbData
->next
= target
->bans
;
2461 target
->bans
->prev
= sbData
;
2462 target
->bans
= sbData
;
2464 /* Update the user counts for the target channel. */
2469 /* Possible to assert (source->bans == NULL) here. */
2470 source
->bans
= NULL
;
2474 merge_data(struct chanData
*source
, struct chanData
*target
)
2476 /* Use more recent visited and owner-transfer time; use older
2477 * registered time. Bitwise or may_opchan. Use higher max.
2478 * Do not touch last_refresh, ban count or user counts.
2480 if(source
->visited
> target
->visited
)
2481 target
->visited
= source
->visited
;
2482 if(source
->registered
< target
->registered
)
2483 target
->registered
= source
->registered
;
2484 if(source
->ownerTransfer
> target
->ownerTransfer
)
2485 target
->ownerTransfer
= source
->ownerTransfer
;
2486 if(source
->may_opchan
)
2487 target
->may_opchan
= 1;
2488 if(source
->max
> target
->max
)
2489 target
->max
= source
->max
;
2493 merge_channel(struct chanData
*source
, struct chanData
*target
)
2495 merge_users(source
, target
);
2496 merge_bans(source
, target
);
2497 merge_data(source
, target
);
2500 static CHANSERV_FUNC(cmd_merge
)
2502 struct userData
*target_user
;
2503 struct chanNode
*target
;
2504 char reason
[MAXLEN
];
2508 /* Make sure the target channel exists and is registered to the user
2509 performing the command. */
2510 if(!(target
= GetChannel(argv
[1])))
2512 reply("MSG_INVALID_CHANNEL");
2516 if(!target
->channel_info
)
2518 reply("CSMSG_NOT_REGISTERED", target
->name
);
2522 if(IsProtected(channel
->channel_info
))
2524 reply("CSMSG_MERGE_NODELETE");
2528 if(IsSuspended(target
->channel_info
))
2530 reply("CSMSG_MERGE_SUSPENDED");
2534 if(channel
== target
)
2536 reply("CSMSG_MERGE_SELF");
2540 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2541 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2543 reply("CSMSG_MERGE_NOT_OWNER");
2547 /* Merge the channel structures and associated data. */
2548 merge_channel(channel
->channel_info
, target
->channel_info
);
2549 spamserv_cs_move_merge(user
, channel
, target
, 0);
2550 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2551 unregister_channel(channel
->channel_info
, reason
);
2552 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2556 static CHANSERV_FUNC(cmd_opchan
)
2558 struct mod_chanmode change
;
2559 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2561 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2564 channel
->channel_info
->may_opchan
= 0;
2565 mod_chanmode_init(&change
);
2567 change
.args
[0].mode
= MODE_CHANOP
;
2568 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2569 mod_chanmode_announce(chanserv
, channel
, &change
);
2570 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2574 static CHANSERV_FUNC(cmd_adduser
)
2576 struct userData
*actee
;
2577 struct userData
*actor
;
2578 struct handle_info
*handle
;
2579 unsigned short access
;
2583 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2585 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2589 access
= user_level_from_name(argv
[2], UL_OWNER
);
2592 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2596 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2597 if(actor
->access
<= access
)
2599 reply("CSMSG_NO_BUMP_ACCESS");
2603 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2605 // 'kevin must first authenticate with AuthServ.' is sent to user
2606 struct userNode
*unode
;
2607 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2610 if(find_adduser_pending(channel
, unode
)) {
2611 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2614 if(IsInChannel(channel
, unode
)) {
2615 reply("CSMSG_ADDUSER_PENDING");
2616 add_adduser_pending(channel
, unode
, access
);
2617 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2619 /* this results in user must auth AND not in chan errors. too confusing..
2621 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2629 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2631 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2635 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2636 scan_user_presence(actee
, NULL
);
2637 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2641 static CHANSERV_FUNC(cmd_clvl
)
2643 struct handle_info
*handle
;
2644 struct userData
*victim
;
2645 struct userData
*actor
;
2646 unsigned short new_access
;
2647 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2651 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2653 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2656 if(handle
== user
->handle_info
&& !privileged
)
2658 reply("CSMSG_NO_SELF_CLVL");
2662 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2664 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2668 if(actor
->access
<= victim
->access
&& !privileged
)
2670 reply("MSG_USER_OUTRANKED", handle
->handle
);
2674 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2678 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2682 if(new_access
>= actor
->access
&& !privileged
)
2684 reply("CSMSG_NO_BUMP_ACCESS");
2688 victim
->access
= new_access
;
2689 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2693 static CHANSERV_FUNC(cmd_deluser
)
2695 struct handle_info
*handle
;
2696 struct userData
*victim
;
2697 struct userData
*actor
;
2698 unsigned short access
;
2703 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2705 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2708 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2710 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2716 access
= user_level_from_name(argv
[1], UL_OWNER
);
2719 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2722 if(access
!= victim
->access
)
2724 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2730 access
= victim
->access
;
2733 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2735 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2739 chan_name
= strdup(channel
->name
);
2740 del_channel_user(victim
, 1);
2741 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2747 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2749 struct userData
*actor
, *uData
, *next
;
2751 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2753 if(min_access
> max_access
)
2755 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2759 if((actor
->access
<= max_access
) && !IsHelping(user
))
2761 reply("CSMSG_NO_ACCESS");
2765 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2769 if((uData
->access
>= min_access
)
2770 && (uData
->access
<= max_access
)
2771 && match_ircglob(uData
->handle
->handle
, mask
))
2772 del_channel_user(uData
, 1);
2775 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2779 static CHANSERV_FUNC(cmd_mdelowner
)
2781 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2784 static CHANSERV_FUNC(cmd_mdelcoowner
)
2786 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2789 static CHANSERV_FUNC(cmd_mdelmanager
)
2791 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2794 static CHANSERV_FUNC(cmd_mdelop
)
2796 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2799 static CHANSERV_FUNC(cmd_mdelpeon
)
2801 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2804 static CHANSERV_FUNC(cmd_mdelhalfop
)
2806 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2812 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2814 struct banData
*bData
, *next
;
2815 char interval
[INTERVALLEN
];
2820 limit
= now
- duration
;
2821 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2825 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2828 del_channel_ban(bData
);
2832 intervalString(interval
, duration
, user
->handle_info
);
2833 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2838 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
)
2840 struct userData
*actor
, *uData
, *next
;
2841 char interval
[INTERVALLEN
];
2845 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2846 if(min_access
> max_access
)
2848 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2852 if((actor
->access
<= max_access
) && !IsHelping(user
))
2854 reply("CSMSG_NO_ACCESS");
2859 limit
= now
- duration
;
2860 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2864 if((uData
->seen
> limit
)
2866 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2869 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2870 || (!max_access
&& (uData
->access
< actor
->access
)))
2872 del_channel_user(uData
, 1);
2880 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2882 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2886 static CHANSERV_FUNC(cmd_trim
)
2888 unsigned long duration
;
2889 unsigned short min_level
, max_level
;
2894 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2895 duration
= ParseInterval(argv
[2]);
2898 reply("CSMSG_CANNOT_TRIM");
2902 if(!irccasecmp(argv
[1], "lamers"))
2904 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
2907 else if(!irccasecmp(argv
[1], "users"))
2909 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
2912 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2914 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
2917 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2919 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
2924 reply("CSMSG_INVALID_TRIM", argv
[1]);
2929 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2930 to the user. cmd_all takes advantage of this. */
2931 static CHANSERV_FUNC(cmd_up
)
2933 struct mod_chanmode change
;
2934 struct userData
*uData
;
2937 mod_chanmode_init(&change
);
2939 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2940 if(!change
.args
[0].u
.member
)
2943 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2947 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2951 reply("CSMSG_GODMODE_UP", argv
[0]);
2954 else if(uData
->access
>= UL_OP
)
2956 change
.args
[0].mode
= MODE_CHANOP
;
2957 errmsg
= "CSMSG_ALREADY_OPPED";
2959 else if(uData
->access
>= UL_HALFOP
)
2961 change
.args
[0].mode
= MODE_HALFOP
;
2962 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2964 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2966 change
.args
[0].mode
= MODE_VOICE
;
2967 errmsg
= "CSMSG_ALREADY_VOICED";
2972 reply("CSMSG_NO_ACCESS");
2975 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2976 if(!change
.args
[0].mode
)
2979 reply(errmsg
, channel
->name
);
2982 modcmd_chanmode_announce(&change
);
2986 static CHANSERV_FUNC(cmd_down
)
2988 struct mod_chanmode change
;
2990 mod_chanmode_init(&change
);
2992 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2993 if(!change
.args
[0].u
.member
)
2996 reply("MSG_CHANNEL_ABSENT", channel
->name
);
3000 if(!change
.args
[0].u
.member
->modes
)
3003 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3007 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3008 modcmd_chanmode_announce(&change
);
3012 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
)
3014 struct userData
*cList
;
3016 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3018 if(IsSuspended(cList
->channel
)
3019 || IsUserSuspended(cList
)
3020 || !GetUserMode(cList
->channel
->channel
, user
))
3023 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3029 static CHANSERV_FUNC(cmd_upall
)
3031 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3034 static CHANSERV_FUNC(cmd_downall
)
3036 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3039 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3040 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3043 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
)
3045 unsigned int ii
, valid
;
3046 struct userNode
*victim
;
3047 struct mod_chanmode
*change
;
3049 change
= mod_chanmode_alloc(argc
- 1);
3051 for(ii
=valid
=0; ++ii
< argc
; )
3053 if(!(victim
= GetUserH(argv
[ii
])))
3055 change
->args
[valid
].mode
= mode
;
3056 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3057 if(!change
->args
[valid
].u
.member
)
3059 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3064 change
->argc
= valid
;
3065 if(valid
< (argc
-1))
3066 reply("CSMSG_PROCESS_FAILED");
3069 modcmd_chanmode_announce(change
);
3070 reply(action
, channel
->name
);
3072 mod_chanmode_free(change
);
3076 static CHANSERV_FUNC(cmd_op
)
3078 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3081 static CHANSERV_FUNC(cmd_hop
)
3083 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3086 static CHANSERV_FUNC(cmd_deop
)
3088 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3091 static CHANSERV_FUNC(cmd_dehop
)
3093 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3096 static CHANSERV_FUNC(cmd_voice
)
3098 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3101 static CHANSERV_FUNC(cmd_devoice
)
3103 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3107 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3113 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3115 struct modeNode
*mn
= channel
->members
.list
[ii
];
3117 if(IsService(mn
->user
))
3120 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3123 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3127 victims
[(*victimCount
)++] = mn
;
3133 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3135 struct userNode
*victim
;
3136 struct modeNode
**victims
;
3137 unsigned int offset
, n
, victimCount
, duration
= 0;
3138 char *reason
= "Bye.", *ban
, *name
;
3139 char interval
[INTERVALLEN
];
3141 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3142 REQUIRE_PARAMS(offset
);
3145 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3146 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3148 /* Truncate the reason to a length of TOPICLEN, as
3149 the ircd does; however, leave room for an ellipsis
3150 and the kicker's nick. */
3151 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3155 if((victim
= GetUserH(argv
[1])))
3157 victims
= alloca(sizeof(victims
[0]));
3158 victims
[0] = GetUserMode(channel
, victim
);
3159 /* XXX: The comparison with ACTION_KICK is just because all
3160 * other actions can work on users outside the channel, and we
3161 * want to allow those (e.g. unbans) in that case. If we add
3162 * some other ejection action for in-channel users, change
3164 victimCount
= victims
[0] ? 1 : 0;
3166 if(IsService(victim
))
3169 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3173 if((action
== ACTION_KICK
) && !victimCount
)
3176 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3180 if(protect_user(victim
, user
, channel
->channel_info
))
3182 // This translates to send_message(user, cmd->parent->bot, ...)
3183 // if user is x3 (ctcp action) cmd is null and segfault.
3185 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3189 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3190 name
= victim
->nick
;
3194 if(!is_ircmask(argv
[1]))
3197 reply("MSG_NICK_UNKNOWN", argv
[1]);
3201 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3203 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3206 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3209 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3210 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3212 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3213 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3214 some creativity, but its not x3's job to be the ban censor anyway. */
3215 if(is_overmask(argv
[1]))
3218 reply("CSMSG_LAME_MASK", argv
[1]);
3222 if((action
== ACTION_KICK
) && (victimCount
== 0))
3225 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3229 name
= ban
= strdup(argv
[1]);
3232 /* Truncate the ban in place if necessary; we must ensure
3233 that 'ban' is a valid ban mask before sanitizing it. */
3234 sanitize_ircmask(ban
);
3236 if(action
& ACTION_ADD_LAMER
)
3238 struct banData
*bData
, *next
;
3240 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3243 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3248 if(action
& ACTION_ADD_TIMED_LAMER
)
3250 duration
= ParseInterval(argv
[2]);
3255 reply("CSMSG_DURATION_TOO_LOW");
3259 else if(duration
> (86400 * 365 * 2))
3262 reply("CSMSG_DURATION_TOO_HIGH");
3269 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3271 if(match_ircglobs(bData
->mask
, ban
))
3273 int exact
= !irccasecmp(bData
->mask
, ban
);
3275 /* The ban is redundant; there is already a ban
3276 with the same effect in place. */
3280 free(bData
->reason
);
3281 bData
->reason
= strdup(reason
);
3282 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3284 reply("CSMSG_REASON_CHANGE", ban
);
3288 if(exact
&& bData
->expires
)
3292 /* If the ban matches an existing one exactly,
3293 extend the expiration time if the provided
3294 duration is longer. */
3295 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3297 bData
->expires
= now
+ duration
;
3308 /* Delete the expiration timeq entry and
3309 requeue if necessary. */
3310 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3313 timeq_add(bData
->expires
, expire_ban
, bData
);
3317 /* automated kickban, dont reply */
3320 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3322 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3328 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3335 if(match_ircglobs(ban
, bData
->mask
))
3337 /* The ban we are adding makes previously existing
3338 bans redundant; silently remove them. */
3339 del_channel_ban(bData
);
3343 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
);
3345 name
= ban
= strdup(bData
->mask
);
3349 /* WHAT DOES THIS DO?? -Rubin */
3350 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3352 extern const char *hidden_host_suffix
;
3353 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3355 unsigned int l1
, l2
;
3358 l2
= strlen(old_name
);
3361 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3363 new_mask
= malloc(MAXLEN
);
3364 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3366 name
= ban
= new_mask
;
3371 if(action
& ACTION_BAN
)
3373 unsigned int exists
;
3374 struct mod_chanmode
*change
;
3376 if(channel
->banlist
.used
>= MAXBANS
)
3379 reply("CSMSG_BANLIST_FULL", channel
->name
);
3384 exists
= ChannelBanExists(channel
, ban
);
3385 change
= mod_chanmode_alloc(victimCount
+ 1);
3386 for(n
= 0; n
< victimCount
; ++n
)
3388 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3389 change
->args
[n
].u
.member
= victims
[n
];
3393 change
->args
[n
].mode
= MODE_BAN
;
3394 change
->args
[n
++].u
.hostmask
= ban
;
3398 modcmd_chanmode_announce(change
);
3400 mod_chanmode_announce(chanserv
, channel
, change
);
3401 mod_chanmode_free(change
);
3403 if(exists
&& (action
== ACTION_BAN
))
3406 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3412 if(action
& ACTION_KICK
)
3414 char kick_reason
[MAXLEN
];
3415 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3417 for(n
= 0; n
< victimCount
; n
++)
3418 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3423 /* No response, since it was automated. */
3425 else if(action
& ACTION_ADD_LAMER
)
3428 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3430 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3432 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3433 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3434 else if(action
& ACTION_BAN
)
3435 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3436 else if(action
& ACTION_KICK
&& victimCount
)
3437 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3443 static CHANSERV_FUNC(cmd_kickban
)
3445 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3448 static CHANSERV_FUNC(cmd_kick
)
3450 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3453 static CHANSERV_FUNC(cmd_ban
)
3455 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3458 static CHANSERV_FUNC(cmd_addlamer
)
3460 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3463 static CHANSERV_FUNC(cmd_addtimedlamer
)
3465 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3468 static struct mod_chanmode
*
3469 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3471 struct mod_chanmode
*change
;
3472 unsigned char *match
;
3473 unsigned int ii
, count
;
3475 match
= alloca(bans
->used
);
3478 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3480 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3481 MATCH_USENICK
| MATCH_VISIBLE
);
3488 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3490 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3497 change
= mod_chanmode_alloc(count
);
3498 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3502 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3503 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3505 assert(count
== change
->argc
);
3509 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3511 unsigned int jj
, ii
, count
;
3513 struct chanData
*channel
;
3515 struct mod_chanmode
*change
;
3517 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3518 /* Walk through every channel */
3519 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3520 switch(channel
->chOpts
[chBanTimeout
])
3522 default: case '0': continue; /* Dont remove bans in this chan */
3523 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3524 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3525 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3526 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3527 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3530 /* First find out how many bans were going to unset */
3531 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3532 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3536 /* At least one ban, so setup a removal */
3537 change
= mod_chanmode_alloc(count
);
3539 /* Walk over every ban in this channel.. */
3540 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3541 bn
= channel
->channel
->banlist
.list
[jj
];
3542 if (bn
->set
< bantimeout
) {
3543 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3545 /* Add this ban to the mode change */
3546 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3547 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3549 /* Pull this ban out of the list */
3550 banList_remove(&(channel
->channel
->banlist
), bn
);
3555 /* Send the modes to IRC */
3556 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3558 /* free memory from strdup above */
3559 for(ii
= 0; ii
< count
; ++ii
)
3560 free((char*)change
->args
[ii
].u
.hostmask
);
3562 mod_chanmode_free(change
);
3565 /* Set this function to run again */
3566 if(chanserv_conf
.ban_timeout_frequency
)
3567 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3572 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3574 struct userNode
*actee
;
3580 /* may want to allow a comma delimited list of users... */
3581 if(!(actee
= GetUserH(argv
[1])))
3583 if(!is_ircmask(argv
[1]))
3585 reply("MSG_NICK_UNKNOWN", argv
[1]);
3589 mask
= strdup(argv
[1]);
3592 /* We don't sanitize the mask here because ircu
3594 if(action
& ACTION_UNBAN
)
3596 struct mod_chanmode
*change
;
3597 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3602 modcmd_chanmode_announce(change
);
3603 for(ii
= 0; ii
< change
->argc
; ++ii
)
3604 free((char*)change
->args
[ii
].u
.hostmask
);
3605 mod_chanmode_free(change
);
3610 if(action
& ACTION_DEL_LAMER
)
3612 struct banData
*ban
, *next
;
3614 ban
= channel
->channel_info
->bans
; /* lamers */
3618 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3621 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3626 del_channel_ban(ban
);
3633 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3635 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3641 static CHANSERV_FUNC(cmd_unban
)
3643 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3646 static CHANSERV_FUNC(cmd_dellamer
)
3648 /* it doesn't necessarily have to remove the channel ban - may want
3649 to make that an option. */
3650 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3653 static CHANSERV_FUNC(cmd_unbanme
)
3655 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3656 long flags
= ACTION_UNBAN
;
3658 /* remove permanent bans if the user has the proper access. */
3659 if(uData
->access
>= UL_MANAGER
)
3660 flags
|= ACTION_DEL_LAMER
;
3662 argv
[1] = user
->nick
;
3663 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3666 static CHANSERV_FUNC(cmd_unbanall
)
3668 struct mod_chanmode
*change
;
3671 if(!channel
->banlist
.used
)
3673 reply("CSMSG_NO_BANS", channel
->name
);
3677 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3678 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3680 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3681 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3683 modcmd_chanmode_announce(change
);
3684 for(ii
= 0; ii
< change
->argc
; ++ii
)
3685 free((char*)change
->args
[ii
].u
.hostmask
);
3686 mod_chanmode_free(change
);
3687 reply("CSMSG_BANS_REMOVED", channel
->name
);
3691 static CHANSERV_FUNC(cmd_open
)
3693 struct mod_chanmode
*change
;
3696 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3698 change
= mod_chanmode_alloc(0);
3699 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3700 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3701 && channel
->channel_info
->modes
.modes_set
)
3702 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3703 modcmd_chanmode_announce(change
);
3704 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3705 for(ii
= 0; ii
< change
->argc
; ++ii
)
3706 free((char*)change
->args
[ii
].u
.hostmask
);
3707 mod_chanmode_free(change
);
3711 static CHANSERV_FUNC(cmd_myaccess
)
3713 static struct string_buffer sbuf
;
3714 struct handle_info
*target_handle
;
3715 struct userData
*uData
;
3718 target_handle
= user
->handle_info
;
3719 else if(!IsHelping(user
))
3721 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3724 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3727 if(!target_handle
->channels
)
3729 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3733 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3734 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3736 struct chanData
*cData
= uData
->channel
;
3738 if(uData
->access
> UL_OWNER
)
3740 if(IsProtected(cData
)
3741 && (target_handle
!= user
->handle_info
)
3742 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3745 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3746 if(uData
->flags
== USER_AUTO_OP
)
3747 string_buffer_append(&sbuf
, ',');
3748 if(IsUserSuspended(uData
))
3749 string_buffer_append(&sbuf
, 's');
3750 if(IsUserAutoOp(uData
))
3752 if(uData
->access
>= UL_OP
)
3753 string_buffer_append(&sbuf
, 'o');
3754 else if(uData
->access
>= UL_HALFOP
)
3755 string_buffer_append(&sbuf
, 'h');
3756 else if(uData
->access
>= UL_PEON
)
3757 string_buffer_append(&sbuf
, 'v');
3759 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3760 string_buffer_append(&sbuf
, 'i');
3761 if(IsUserAutoJoin(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3762 string_buffer_append(&sbuf
, 'j');
3764 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3766 string_buffer_append_string(&sbuf
, ")]");
3767 string_buffer_append(&sbuf
, '\0');
3768 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3774 static CHANSERV_FUNC(cmd_access
)
3776 struct userNode
*target
;
3777 struct handle_info
*target_handle
;
3778 struct userData
*uData
;
3780 char prefix
[MAXLEN
];
3785 target_handle
= target
->handle_info
;
3787 else if((target
= GetUserH(argv
[1])))
3789 target_handle
= target
->handle_info
;
3791 else if(argv
[1][0] == '*')
3793 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3795 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3801 reply("MSG_NICK_UNKNOWN", argv
[1]);
3805 assert(target
|| target_handle
);
3807 if(target
== chanserv
)
3809 reply("CSMSG_IS_CHANSERV");
3817 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3822 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3825 reply("MSG_AUTHENTICATE");
3831 const char *epithet
= NULL
, *type
= NULL
;
3834 epithet
= chanserv_conf
.irc_operator_epithet
;
3837 else if(IsNetworkHelper(target
))
3839 epithet
= chanserv_conf
.network_helper_epithet
;
3840 type
= "network helper";
3842 else if(IsSupportHelper(target
))
3844 epithet
= chanserv_conf
.support_helper_epithet
;
3845 type
= "support helper";
3849 if(target_handle
->epithet
)
3850 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3852 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3854 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3858 sprintf(prefix
, "%s", target_handle
->handle
);
3861 if(!channel
->channel_info
)
3863 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3867 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3868 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3869 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3871 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3872 /* To prevent possible information leaks, only show infolines
3873 * if the requestor is in the channel or it's their own
3875 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3877 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3879 /* Likewise, only say it's suspended if the user has active
3880 * access in that channel or it's their own entry. */
3881 if(IsUserSuspended(uData
)
3882 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3883 || (user
->handle_info
== uData
->handle
)))
3885 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3890 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3896 /* This is never used...
3898 zoot_list(struct listData *list)
3900 struct userData *uData;
3901 unsigned int start, curr, highest, lowest;
3902 struct helpfile_table tmp_table;
3903 const char **temp, *msg;
3905 if(list->table.length == 1)
3908 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);
3910 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));
3911 msg = user_find_message(list->user, "MSG_NONE");
3912 send_message_type(4, list->user, list->bot, " %s", msg);
3914 tmp_table.width = list->table.width;
3915 tmp_table.flags = list->table.flags;
3916 list->table.contents[0][0] = " ";
3917 highest = list->highest;
3918 if(list->lowest != 0)
3919 lowest = list->lowest;
3920 else if(highest < 100)
3923 lowest = highest - 100;
3924 for(start = curr = 1; curr < list->table.length; )
3926 uData = list->users[curr-1];
3927 list->table.contents[curr++][0] = " ";
3928 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3931 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);
3933 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));
3934 temp = list->table.contents[--start];
3935 list->table.contents[start] = list->table.contents[0];
3936 tmp_table.contents = list->table.contents + start;
3937 tmp_table.length = curr - start;
3938 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3939 list->table.contents[start] = temp;
3941 highest = lowest - 1;
3942 lowest = (highest < 100) ? 0 : (highest - 99);
3949 normal_list(struct listData
*list
)
3953 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
);
3955 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
));
3956 if(list
->table
.length
== 1)
3958 msg
= user_find_message(list
->user
, "MSG_NONE");
3959 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3962 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3965 /* if these need changed, uncomment and customize
3967 clean_list(struct listData *list)
3971 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);
3973 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));
3974 if(list->table.length == 1)
3976 msg = user_find_message(list->user, "MSG_NONE");
3977 send_message_type(4, list->user, list->bot, " %s", msg);
3980 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3984 advanced_list(struct listData *list)
3988 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);
3990 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));
3991 if(list->table.length == 1)
3993 msg = user_find_message(list->user, "MSG_NONE");
3994 send_message_type(4, list->user, list->bot, " %s", msg);
3997 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4001 classic_list(struct listData *list)
4005 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4007 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4008 if(list->table.length == 1)
4010 msg = user_find_message(list->user, "MSG_NONE");
4011 send_message_type(4, list->user, list->bot, " %s", msg);
4014 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4019 userData_access_comp(const void *arg_a
, const void *arg_b
)
4021 const struct userData
*a
= *(struct userData
**)arg_a
;
4022 const struct userData
*b
= *(struct userData
**)arg_b
;
4024 if(a
->access
!= b
->access
)
4025 res
= b
->access
- a
->access
;
4027 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4032 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4034 void (*send_list
)(struct listData
*);
4035 struct userData
*uData
;
4036 struct listData lData
;
4037 unsigned int matches
;
4043 lData
.bot
= cmd
->parent
->bot
;
4044 lData
.channel
= channel
;
4045 lData
.lowest
= lowest
;
4046 lData
.highest
= highest
;
4047 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4048 send_list
= normal_list
;
4049 /* What does the following line do exactly?? */
4050 /*(void)zoot_list; ** since it doesn't show user levels */
4053 if(user->handle_info)
4055 switch(user->handle_info->userlist_style)
4057 case HI_STYLE_CLEAN:
4058 send_list = clean_list;
4060 case HI_STYLE_ADVANCED:
4061 send_list = advanced_list;
4063 case HI_STYLE_CLASSIC:
4064 send_list = classic_list;
4066 case HI_STYLE_NORMAL:
4068 send_list = normal_list;
4073 send_list
= normal_list
;
4075 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4077 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4079 if((uData
->access
< lowest
)
4080 || (uData
->access
> highest
)
4081 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4083 lData
.users
[matches
++] = uData
;
4085 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4087 lData
.table
.length
= matches
+1;
4088 lData
.table
.flags
= TABLE_NO_FREE
;
4089 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4091 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4092 lData
.table
.width
= 5; /* with level = 5 */
4094 lData
.table
.width
= 4; /* without = 4 */
4095 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4096 lData
.table
.contents
[0] = ary
;
4097 if(user
->handle_info
) {
4098 switch(user
->handle_info
->userlist_style
) {
4099 case HI_STYLE_CLASSIC
:
4102 case HI_STYLE_ADVANCED
:
4103 ary
[i
++] = "Access";
4106 case HI_STYLE_CLEAN
:
4107 ary
[i
++] = "Access";
4109 case HI_STYLE_NORMAL
:
4111 ary
[i
++] = "Access";
4116 ary
[i
++] = "Access";
4118 ary
[i
++] = "Account";
4119 ary
[i
] = "Last Seen";
4121 ary
[i
++] = "Status";
4122 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4124 struct userData
*uData
= lData
.users
[matches
-1];
4125 char seen
[INTERVALLEN
];
4128 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4129 lData
.table
.contents
[matches
] = ary
;
4130 if(user
->handle_info
) {
4131 switch(user
->handle_info
->userlist_style
) {
4132 case HI_STYLE_CLASSIC
:
4133 ary
[i
++] = strtab(uData
->access
);
4135 case HI_STYLE_ADVANCED
:
4136 ary
[i
++] = user_level_name_from_level(uData
->access
);
4137 ary
[i
++] = strtab(uData
->access
);
4139 case HI_STYLE_CLEAN
:
4140 ary
[i
++] = user_level_name_from_level(uData
->access
);
4142 case HI_STYLE_NORMAL
:
4144 ary
[i
++] = user_level_name_from_level(uData
->access
);
4149 ary
[i
++] = user_level_name_from_level(uData
->access
);
4151 ary
[i
++] = uData
->handle
->handle
;
4154 else if(!uData
->seen
)
4157 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4158 ary
[i
] = strdup(ary
[i
]);
4160 if(IsUserSuspended(uData
))
4161 ary
[i
++] = "Suspended";
4162 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4163 ary
[i
++] = "Vacation";
4165 ary
[i
++] = "Normal";
4168 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4170 /* Free strdup above */
4171 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4172 free(lData
.table
.contents
[matches
]);
4174 free(lData
.table
.contents
[0]);
4175 free(lData
.table
.contents
);
4179 /* Remove this now that debugging is over? or improve it for
4180 * users? Would it be better tied into USERS somehow? -Rubin */
4181 static CHANSERV_FUNC(cmd_pending
)
4183 struct adduserPending
*ap
;
4184 reply("CSMSG_ADDUSER_PENDING_HEADER");
4185 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4187 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4188 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4189 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4193 static CHANSERV_FUNC(cmd_users
)
4195 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4198 static CHANSERV_FUNC(cmd_wlist
)
4200 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4203 static CHANSERV_FUNC(cmd_clist
)
4205 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4208 static CHANSERV_FUNC(cmd_mlist
)
4210 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4213 static CHANSERV_FUNC(cmd_olist
)
4215 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4218 static CHANSERV_FUNC(cmd_hlist
)
4220 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4223 static CHANSERV_FUNC(cmd_plist
)
4225 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4228 static CHANSERV_FUNC(cmd_lamers
)
4230 struct helpfile_table tbl
;
4231 unsigned int matches
= 0, timed
= 0, ii
;
4232 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4233 const char *msg_never
, *triggered
, *expires
;
4234 struct banData
*ban
, **bans
; /* lamers */
4241 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4242 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4245 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4247 if(search
&& !match_ircglobs(search
, ban
->mask
))
4249 bans
[matches
++] = ban
;
4254 tbl
.length
= matches
+ 1;
4255 tbl
.width
= 4 + timed
;
4257 tbl
.flags
= TABLE_NO_FREE
;
4258 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4259 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4260 tbl
.contents
[0][0] = "Mask";
4261 tbl
.contents
[0][1] = "Set By";
4262 tbl
.contents
[0][2] = "Triggered";
4265 tbl
.contents
[0][3] = "Expires";
4266 tbl
.contents
[0][4] = "Reason";
4269 tbl
.contents
[0][3] = "Reason";
4272 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4274 free(tbl
.contents
[0]);
4279 msg_never
= user_find_message(user
, "MSG_NEVER");
4280 for(ii
= 0; ii
< matches
; )
4286 else if(ban
->expires
)
4287 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4289 expires
= msg_never
;
4292 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4294 triggered
= msg_never
;
4296 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4297 tbl
.contents
[ii
][0] = ban
->mask
;
4298 tbl
.contents
[ii
][1] = ban
->owner
;
4299 tbl
.contents
[ii
][2] = strdup(triggered
);
4302 tbl
.contents
[ii
][3] = strdup(expires
);
4303 tbl
.contents
[ii
][4] = ban
->reason
;
4306 tbl
.contents
[ii
][3] = ban
->reason
;
4308 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4309 /* reply("MSG_MATCH_COUNT", matches); */
4310 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4312 free((char*)tbl
.contents
[ii
][2]);
4314 free((char*)tbl
.contents
[ii
][3]);
4315 free(tbl
.contents
[ii
]);
4317 free(tbl
.contents
[0]);
4324 * return + if the user does NOT have the right to set the topic, and
4325 * the topic is changed.
4328 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4330 struct chanData
*cData
= channel
->channel_info
;
4331 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4333 else if(cData
->topic
)
4334 return irccasecmp(new_topic
, cData
->topic
);
4341 * Makes a givin topic fit into a givin topic mask and returns
4344 * topic_mask - the mask to conform to
4345 * topic - the topic to make conform
4346 * new_topic - the pre-allocated char* to put the new topic into
4348 * modifies: new_topic
4351 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4353 //char *topic_mask = cData->topic_mask;
4355 int pos
=0, starpos
=-1, dpos
=0, len
;
4357 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4364 strcpy(new_topic
, "");
4367 len
= strlen(topic
);
4368 if((dpos
+ len
) > TOPICLEN
)
4369 len
= TOPICLEN
+ 1 - dpos
;
4370 memcpy(new_topic
+dpos
, topic
, len
);
4374 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4375 default: new_topic
[dpos
++] = tchar
; break;
4378 if((dpos
> TOPICLEN
) || tchar
)
4380 strcpy(new_topic
, "");
4383 new_topic
[dpos
] = 0;
4387 static CHANSERV_FUNC(cmd_topic
)
4389 struct chanData
*cData
;
4393 #ifdef WITH_PROTOCOL_P10
4397 cData
= channel
->channel_info
;
4402 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4403 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4404 reply("CSMSG_TOPIC_SET", cData
->topic
);
4408 reply("CSMSG_NO_TOPIC", channel
->name
);
4412 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4413 /* If they say "!topic *", use an empty topic. */
4414 if((topic
[0] == '*') && (topic
[1] == 0))
4417 if(bad_topic(channel
, user
, topic
))
4419 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4424 /* If there is a topicmask set, and the new topic doesnt match, make it */
4425 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4427 char *topic_mask
= cData
->topic_mask
;
4428 char new_topic
[TOPICLEN
+1];
4430 /* make a new topic fitting mask */
4431 conform_topic(topic_mask
, topic
, new_topic
);
4434 /* Topic couldnt fit into mask, was too long */
4435 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4436 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4439 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4441 else /* No mask set, just set the topic */
4442 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4445 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4447 /* Grab the topic and save it as the default topic. */
4449 cData
->topic
= strdup(channel
->topic
);
4455 static CHANSERV_FUNC(cmd_mode
)
4457 struct userData
*uData
;
4458 struct mod_chanmode
*change
;
4463 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4464 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4468 change
= &channel
->channel_info
->modes
;
4469 if(change
->modes_set
|| change
->modes_clear
) {
4470 modcmd_chanmode_announce(change
);
4471 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4473 reply("CSMSG_NO_MODES", channel
->name
);
4477 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4479 base_oplevel
= MAXOPLEVEL
;
4480 else if (uData
->access
>= UL_OWNER
)
4483 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4484 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4488 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4492 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4493 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4496 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4497 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4501 modcmd_chanmode_announce(change
);
4502 mod_chanmode_free(change
);
4503 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4507 static CHANSERV_FUNC(cmd_invite
)
4509 struct userData
*uData
;
4510 struct userNode
*invite
;
4512 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4516 if(!(invite
= GetUserH(argv
[1])))
4518 reply("MSG_NICK_UNKNOWN", argv
[1]);
4525 if(GetUserMode(channel
, invite
))
4527 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4535 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4536 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4539 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4542 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4544 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4545 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4546 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4552 irc_invite(chanserv
, invite
, channel
);
4554 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4559 static CHANSERV_FUNC(cmd_inviteme
)
4561 if(GetUserMode(channel
, user
))
4563 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4566 if(channel
->channel_info
4567 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4569 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4572 irc_invite(cmd
->parent
->bot
, user
, channel
);
4577 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4580 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4582 /* We display things based on two dimensions:
4583 * - Issue time: present or absent
4584 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4585 * (in order of precedence, so something both expired and revoked
4586 * only counts as revoked)
4588 combo
= (suspended
->issued
? 4 : 0)
4589 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4591 case 0: /* no issue time, indefinite expiration */
4592 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4594 case 1: /* no issue time, expires in future */
4595 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4596 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4598 case 2: /* no issue time, expired */
4599 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4600 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4602 case 3: /* no issue time, revoked */
4603 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4604 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4606 case 4: /* issue time set, indefinite expiration */
4607 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4608 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4610 case 5: /* issue time set, expires in future */
4611 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4612 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4613 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4615 case 6: /* issue time set, expired */
4616 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4617 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4618 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4620 case 7: /* issue time set, revoked */
4621 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4622 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4623 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4626 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4632 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4635 const char *fmt
= "%a %b %d %H:%M %Y";
4636 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4638 if(giveownership
->staff_issuer
)
4640 if(giveownership
->reason
)
4641 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4642 giveownership
->target
, giveownership
->target_access
,
4643 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4645 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4646 giveownership
->target
, giveownership
->target_access
,
4647 giveownership
->staff_issuer
, buf
);
4651 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4656 static CHANSERV_FUNC(cmd_info
)
4658 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4659 struct userData
*uData
, *owner
;
4660 struct chanData
*cData
;
4661 struct do_not_register
*dnr
;
4666 cData
= channel
->channel_info
;
4667 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4668 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4671 uData
= GetChannelUser(cData
, user
->handle_info
);
4672 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4674 mod_chanmode_format(&cData
->modes
, modes
);
4675 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4676 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4679 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4683 note
= iter_data(it
);
4684 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4687 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4688 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4691 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4692 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4693 if(owner
->access
== UL_OWNER
)
4694 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4695 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4696 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4697 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4699 privileged
= IsStaff(user
);
4700 /* if(privileged) */
4701 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4702 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4703 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4705 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4706 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4708 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4710 struct suspended
*suspended
;
4711 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4712 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4713 show_suspension_info(cmd
, user
, suspended
);
4715 else if(IsSuspended(cData
))
4717 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4718 show_suspension_info(cmd
, user
, cData
->suspended
);
4720 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4722 struct giveownership
*giveownership
;
4723 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4724 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4725 show_giveownership_info(cmd
, user
, giveownership
);
4727 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4728 reply("CSMSG_CHANNEL_END");
4730 reply("CSMSG_CHANNEL_END_CLEAN");
4734 static CHANSERV_FUNC(cmd_netinfo
)
4736 extern time_t boot_time
;
4737 extern unsigned long burst_length
;
4738 char interval
[INTERVALLEN
];
4740 reply("CSMSG_NETWORK_INFO");
4741 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4742 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4743 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4744 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4745 reply("CSMSG_NETWORK_LAMERS", banCount
);
4746 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4747 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4748 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4753 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4755 struct helpfile_table table
;
4757 struct userNode
*user
;
4762 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4763 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4764 for(nn
=0; nn
<list
->used
; nn
++)
4766 user
= list
->list
[nn
];
4767 if(user
->modes
& skip_flags
)
4771 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4774 nick
= alloca(strlen(user
->nick
)+3);
4775 sprintf(nick
, "(%s)", user
->nick
);
4779 table
.contents
[table
.length
][0] = nick
;
4782 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4785 static CHANSERV_FUNC(cmd_ircops
)
4787 reply("CSMSG_STAFF_OPERS");
4788 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4792 static CHANSERV_FUNC(cmd_helpers
)
4794 reply("CSMSG_STAFF_HELPERS");
4795 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4799 static CHANSERV_FUNC(cmd_staff
)
4801 reply("CSMSG_NETWORK_STAFF");
4802 cmd_ircops(CSFUNC_ARGS
);
4803 cmd_helpers(CSFUNC_ARGS
);
4807 static CHANSERV_FUNC(cmd_peek
)
4809 struct modeNode
*mn
;
4810 char modes
[MODELEN
];
4812 struct helpfile_table table
;
4814 irc_make_chanmode(channel
, modes
);
4816 reply("CSMSG_PEEK_INFO", channel
->name
);
4817 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4819 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4820 reply("CSMSG_PEEK_MODES", modes
);
4821 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4825 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4826 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4827 for(n
= 0; n
< channel
->members
.used
; n
++)
4829 mn
= channel
->members
.list
[n
];
4830 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4832 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4833 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4838 reply("CSMSG_PEEK_OPS");
4839 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4842 reply("CSMSG_PEEK_NO_OPS");
4843 reply("CSMSG_PEEK_END");
4847 static MODCMD_FUNC(cmd_wipeinfo
)
4849 struct handle_info
*victim
;
4850 struct userData
*ud
, *actor
;
4853 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4854 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4856 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4858 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4861 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4863 reply("MSG_USER_OUTRANKED", victim
->handle
);
4869 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4874 resync_channel(struct chanNode
*channel
)
4876 struct mod_chanmode
*changes
;
4877 struct chanData
*cData
= channel
->channel_info
;
4878 unsigned int ii
, used
;
4880 /* 6 = worst case -ovh+ovh on everyone */
4881 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4882 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4884 struct modeNode
*mn
= channel
->members
.list
[ii
];
4885 struct userData
*uData
;
4887 if(IsService(mn
->user
))
4891 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4893 /* If the channel is in no-mode mode, de-mode EVERYONE */
4894 if(cData
->chOpts
[chAutomode
] == 'n')
4898 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4899 changes
->args
[used
++].u
.member
= mn
;
4902 else /* Give various userlevels their modes.. */
4904 if(uData
&& uData
->access
>= UL_OP
)
4906 if(!(mn
->modes
& MODE_CHANOP
))
4908 changes
->args
[used
].mode
= MODE_CHANOP
;
4909 changes
->args
[used
++].u
.member
= mn
;
4912 else if(uData
&& uData
->access
>= UL_HALFOP
)
4914 if(mn
->modes
& MODE_CHANOP
)
4916 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4917 changes
->args
[used
++].u
.member
= mn
;
4919 if(!(mn
->modes
& MODE_HALFOP
))
4921 changes
->args
[used
].mode
= MODE_HALFOP
;
4922 changes
->args
[used
++].u
.member
= mn
;
4925 else if(uData
&& uData
->access
>= UL_PEON
)
4927 if(mn
->modes
& MODE_CHANOP
)
4929 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4930 changes
->args
[used
++].u
.member
= mn
;
4932 if(mn
->modes
& MODE_HALFOP
)
4934 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4935 changes
->args
[used
++].u
.member
= mn
;
4937 /* Don't voice peons if were in mode m */
4938 if( cData
->chOpts
[chAutomode
] == 'm')
4940 if(mn
->modes
& MODE_VOICE
)
4942 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4943 changes
->args
[used
++].u
.member
= mn
;
4946 /* otherwise, make user they do have voice */
4947 else if(!(mn
->modes
& MODE_VOICE
))
4949 changes
->args
[used
].mode
= MODE_VOICE
;
4950 changes
->args
[used
++].u
.member
= mn
;
4953 else /* They arnt on the userlist.. */
4955 /* If we voice everyone, but they dont.. */
4956 if(cData
->chOpts
[chAutomode
] == 'v')
4958 /* Remove anything except v */
4959 if(mn
->modes
& ~MODE_VOICE
)
4961 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4962 changes
->args
[used
++].u
.member
= mn
;
4965 if(!(mn
->modes
& MODE_VOICE
))
4967 changes
->args
[used
].mode
= MODE_VOICE
;
4968 changes
->args
[used
++].u
.member
= mn
;
4971 /* If we hop everyone, but they dont.. */
4972 else if(cData
->chOpts
[chAutomode
] == 'h')
4974 /* Remove anything except h */
4975 if(mn
->modes
& ~MODE_HALFOP
)
4977 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4978 changes
->args
[used
++].u
.member
= mn
;
4981 if(!(mn
->modes
& MODE_HALFOP
))
4983 changes
->args
[used
].mode
= MODE_HALFOP
;
4984 changes
->args
[used
++].u
.member
= mn
;
4987 /* If we op everyone, but they dont.. */
4988 else if(cData
->chOpts
[chAutomode
] == 'o')
4990 /* Remove anything except h */
4991 if(mn
->modes
& ~MODE_CHANOP
)
4993 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4994 changes
->args
[used
++].u
.member
= mn
;
4997 if(!(mn
->modes
& MODE_CHANOP
))
4999 changes
->args
[used
].mode
= MODE_CHANOP
;
5000 changes
->args
[used
++].u
.member
= mn
;
5003 /* they have no excuse for having modes, de-everything them */
5008 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5009 changes
->args
[used
++].u
.member
= mn
;
5015 changes
->argc
= used
;
5016 mod_chanmode_announce(chanserv
, channel
, changes
);
5017 mod_chanmode_free(changes
);
5020 static CHANSERV_FUNC(cmd_resync
)
5022 resync_channel(channel
);
5023 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5027 static CHANSERV_FUNC(cmd_seen
)
5029 struct userData
*uData
;
5030 struct handle_info
*handle
;
5031 char seen
[INTERVALLEN
];
5035 if(!irccasecmp(argv
[1], chanserv
->nick
))
5037 reply("CSMSG_IS_CHANSERV");
5041 if(!(handle
= get_handle_info(argv
[1])))
5043 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5047 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5049 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5054 reply("CSMSG_USER_PRESENT", handle
->handle
);
5055 else if(uData
->seen
)
5056 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5058 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5060 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5061 reply("CSMSG_USER_VACATION", handle
->handle
);
5066 static MODCMD_FUNC(cmd_names
)
5068 struct userNode
*targ
;
5069 struct userData
*targData
;
5070 unsigned int ii
, pos
;
5073 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5075 targ
= channel
->members
.list
[ii
]->user
;
5076 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5079 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5082 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5086 if(IsUserSuspended(targData
))
5088 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5091 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5092 reply("CSMSG_END_NAMES", channel
->name
);
5097 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5099 switch(ntype
->visible_type
)
5101 case NOTE_VIS_ALL
: return 1;
5102 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5103 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5108 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5110 struct userData
*uData
;
5112 switch(ntype
->set_access_type
)
5114 case NOTE_SET_CHANNEL_ACCESS
:
5115 if(!user
->handle_info
)
5117 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5119 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5120 case NOTE_SET_CHANNEL_SETTER
:
5121 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5122 case NOTE_SET_PRIVILEGED
: default:
5123 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5127 static CHANSERV_FUNC(cmd_note
)
5129 struct chanData
*cData
;
5131 struct note_type
*ntype
;
5133 cData
= channel
->channel_info
;
5136 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5140 /* If no arguments, show all visible notes for the channel. */
5146 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5148 note
= iter_data(it
);
5149 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5152 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5153 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5156 reply("CSMSG_NOTELIST_END", channel
->name
);
5158 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5160 /* If one argument, show the named note. */
5163 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5164 && note_type_visible_to_user(cData
, note
->type
, user
))
5166 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5168 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5169 && note_type_visible_to_user(NULL
, ntype
, user
))
5171 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5176 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5180 /* Assume they're trying to set a note. */
5184 ntype
= dict_find(note_types
, argv
[1], NULL
);
5187 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5190 else if(note_type_settable_by_user(channel
, ntype
, user
))
5192 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5193 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5194 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5195 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5196 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5198 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5200 /* The note is viewable to staff only, so return 0
5201 to keep the invocation from getting logged (or
5202 regular users can see it in !events). */
5208 reply("CSMSG_NO_ACCESS");
5215 static CHANSERV_FUNC(cmd_delnote
)
5220 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5221 || !note_type_settable_by_user(channel
, note
->type
, user
))
5223 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5226 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5227 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5231 static CHANSERV_FUNC(cmd_last
)
5237 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5239 if(numoflines
< 1 || numoflines
> 200)
5241 reply("CSMSG_LAST_INVALID");
5244 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5248 static CHANSERV_FUNC(cmd_events
)
5250 struct logSearch discrim
;
5251 struct logReport report
;
5252 unsigned int matches
, limit
;
5254 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5255 if(limit
< 1 || limit
> 200)
5258 memset(&discrim
, 0, sizeof(discrim
));
5259 discrim
.masks
.bot
= chanserv
;
5260 discrim
.masks
.channel_name
= channel
->name
;
5262 discrim
.masks
.command
= argv
[2];
5263 discrim
.limit
= limit
;
5264 discrim
.max_time
= INT_MAX
;
5265 discrim
.severities
= 1 << LOG_COMMAND
;
5266 report
.reporter
= chanserv
;
5268 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5269 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5271 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5273 reply("MSG_MATCH_COUNT", matches
);
5275 reply("MSG_NO_MATCHES");
5279 static CHANSERV_FUNC(cmd_say
)
5285 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5286 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5288 else if(GetUserH(argv
[1]))
5291 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5292 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5296 reply("MSG_NOT_TARGET_NAME");
5302 static CHANSERV_FUNC(cmd_emote
)
5308 /* CTCP is so annoying. */
5309 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5310 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5312 else if(GetUserH(argv
[1]))
5314 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5315 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5319 reply("MSG_NOT_TARGET_NAME");
5325 struct channelList
*
5326 chanserv_support_channels(void)
5328 return &chanserv_conf
.support_channels
;
5331 static CHANSERV_FUNC(cmd_expire
)
5333 int channel_count
= registered_channels
;
5334 expire_channels(NULL
);
5335 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5340 chanserv_expire_suspension(void *data
)
5342 struct suspended
*suspended
= data
;
5343 struct chanNode
*channel
;
5345 if(!suspended
->expires
|| (now
< suspended
->expires
))
5346 suspended
->revoked
= now
;
5347 channel
= suspended
->cData
->channel
;
5348 suspended
->cData
->channel
= channel
;
5349 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5350 if(!IsOffChannel(suspended
->cData
))
5352 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5353 ss_cs_join_channel(channel
, 1);
5357 static CHANSERV_FUNC(cmd_csuspend
)
5359 struct suspended
*suspended
;
5360 char reason
[MAXLEN
];
5361 time_t expiry
, duration
;
5362 struct userData
*uData
;
5366 if(IsProtected(channel
->channel_info
))
5368 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5372 if(argv
[1][0] == '!')
5374 else if(IsSuspended(channel
->channel_info
))
5376 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5377 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5381 if(!strcmp(argv
[1], "0"))
5383 else if((duration
= ParseInterval(argv
[1])))
5384 expiry
= now
+ duration
;
5387 reply("MSG_INVALID_DURATION", argv
[1]);
5391 unsplit_string(argv
+ 2, argc
- 2, reason
);
5393 suspended
= calloc(1, sizeof(*suspended
));
5394 suspended
->revoked
= 0;
5395 suspended
->issued
= now
;
5396 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5397 suspended
->expires
= expiry
;
5398 suspended
->reason
= strdup(reason
);
5399 suspended
->cData
= channel
->channel_info
;
5400 suspended
->previous
= suspended
->cData
->suspended
;
5401 suspended
->cData
->suspended
= suspended
;
5403 if(suspended
->expires
)
5404 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5406 if(IsSuspended(channel
->channel_info
))
5408 suspended
->previous
->revoked
= now
;
5409 if(suspended
->previous
->expires
)
5410 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5412 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5413 channel
->name
, suspended
->suspender
);
5417 /* Mark all users in channel as absent. */
5418 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5427 /* Mark the channel as suspended, then part. */
5428 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5429 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5430 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5431 reply("CSMSG_SUSPENDED", channel
->name
);
5432 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5433 channel
->name
, suspended
->suspender
);
5438 static CHANSERV_FUNC(cmd_cunsuspend
)
5440 struct suspended
*suspended
;
5442 if(!IsSuspended(channel
->channel_info
))
5444 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5448 suspended
= channel
->channel_info
->suspended
;
5450 /* Expire the suspension and join ChanServ to the channel. */
5451 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5452 chanserv_expire_suspension(suspended
);
5453 reply("CSMSG_UNSUSPENDED", channel
->name
);
5454 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5455 channel
->name
, user
->handle_info
->handle
);
5459 typedef struct chanservSearch
5467 unsigned long flags
;
5471 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5474 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5479 search
= malloc(sizeof(struct chanservSearch
));
5480 memset(search
, 0, sizeof(*search
));
5483 for(i
= 0; i
< argc
; i
++)
5485 /* Assume all criteria require arguments. */
5488 reply("MSG_MISSING_PARAMS", argv
[i
]);
5492 if(!irccasecmp(argv
[i
], "name"))
5493 search
->name
= argv
[++i
];
5494 else if(!irccasecmp(argv
[i
], "registrar"))
5495 search
->registrar
= argv
[++i
];
5496 else if(!irccasecmp(argv
[i
], "unvisited"))
5497 search
->unvisited
= ParseInterval(argv
[++i
]);
5498 else if(!irccasecmp(argv
[i
], "registered"))
5499 search
->registered
= ParseInterval(argv
[++i
]);
5500 else if(!irccasecmp(argv
[i
], "flags"))
5503 if(!irccasecmp(argv
[i
], "nodelete"))
5504 search
->flags
|= CHANNEL_NODELETE
;
5505 else if(!irccasecmp(argv
[i
], "suspended"))
5506 search
->flags
|= CHANNEL_SUSPENDED
;
5509 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5513 else if(!irccasecmp(argv
[i
], "limit"))
5514 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5517 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5522 if(search
->name
&& !strcmp(search
->name
, "*"))
5524 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5525 search
->registrar
= 0;
5534 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5536 const char *name
= channel
->channel
->name
;
5537 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5538 (search
->registrar
&& !channel
->registrar
) ||
5539 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5540 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5541 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5542 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5549 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5551 struct chanData
*channel
;
5552 unsigned int matches
= 0;
5554 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5556 if(!chanserv_channel_match(channel
, search
))
5566 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5571 search_print(struct chanData
*channel
, void *data
)
5573 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5576 static CHANSERV_FUNC(cmd_search
)
5579 unsigned int matches
;
5580 channel_search_func action
;
5584 if(!irccasecmp(argv
[1], "count"))
5585 action
= search_count
;
5586 else if(!irccasecmp(argv
[1], "print"))
5587 action
= search_print
;
5590 reply("CSMSG_ACTION_INVALID", argv
[1]);
5594 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5598 if(action
== search_count
)
5599 search
->limit
= INT_MAX
;
5601 if(action
== search_print
)
5603 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5604 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5608 matches
= chanserv_channel_search(search
, action
, user
);
5611 reply("MSG_MATCH_COUNT", matches
);
5613 reply("MSG_NO_MATCHES");
5619 static CHANSERV_FUNC(cmd_unvisited
)
5621 struct chanData
*cData
;
5622 time_t interval
= chanserv_conf
.channel_expire_delay
;
5623 char buffer
[INTERVALLEN
];
5624 unsigned int limit
= 25, matches
= 0;
5628 interval
= ParseInterval(argv
[1]);
5630 limit
= atoi(argv
[2]);
5633 intervalString(buffer
, interval
, user
->handle_info
);
5634 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5636 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5638 if((now
- cData
->visited
) < interval
)
5641 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5642 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5649 static MODCMD_FUNC(chan_opt_defaulttopic
)
5655 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5657 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5661 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5663 free(channel
->channel_info
->topic
);
5664 if(topic
[0] == '*' && topic
[1] == 0)
5666 topic
= channel
->channel_info
->topic
= NULL
;
5670 topic
= channel
->channel_info
->topic
= strdup(topic
);
5671 if(channel
->channel_info
->topic_mask
5672 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5673 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5675 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5678 if(channel
->channel_info
->topic
)
5679 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5681 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5685 static MODCMD_FUNC(chan_opt_topicmask
)
5689 struct chanData
*cData
= channel
->channel_info
;
5692 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5694 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5698 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5700 if(cData
->topic_mask
)
5701 free(cData
->topic_mask
);
5702 if(mask
[0] == '*' && mask
[1] == 0)
5704 cData
->topic_mask
= 0;
5708 cData
->topic_mask
= strdup(mask
);
5710 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5711 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5712 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5716 if(channel
->channel_info
->topic_mask
)
5717 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5719 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5723 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5727 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5731 if(greeting
[0] == '*' && greeting
[1] == 0)
5735 unsigned int length
= strlen(greeting
);
5736 if(length
> chanserv_conf
.greeting_length
)
5738 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5741 *data
= strdup(greeting
);
5750 reply(name
, user_find_message(user
, "MSG_NONE"));
5754 static MODCMD_FUNC(chan_opt_greeting
)
5756 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5759 static MODCMD_FUNC(chan_opt_usergreeting
)
5761 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5764 static MODCMD_FUNC(chan_opt_modes
)
5766 struct mod_chanmode
*new_modes
;
5767 char modes
[MODELEN
];
5771 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5772 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5776 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5778 reply("CSMSG_NO_ACCESS");
5781 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5783 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5785 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5787 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5790 else if(new_modes
->argc
> 1)
5792 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5793 mod_chanmode_free(new_modes
);
5798 channel
->channel_info
->modes
= *new_modes
;
5799 modcmd_chanmode_announce(new_modes
);
5800 mod_chanmode_free(new_modes
);
5804 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5806 reply("CSMSG_SET_MODES", modes
);
5808 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5812 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5814 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5816 struct chanData
*cData
= channel
->channel_info
;
5821 /* Set flag according to value. */
5822 if(enabled_string(argv
[1]))
5824 cData
->flags
|= mask
;
5827 else if(disabled_string(argv
[1]))
5829 cData
->flags
&= ~mask
;
5834 reply("MSG_INVALID_BINARY", argv
[1]);
5840 /* Find current option value. */
5841 value
= (cData
->flags
& mask
) ? 1 : 0;
5845 reply(name
, user_find_message(user
, "MSG_ON"));
5847 reply(name
, user_find_message(user
, "MSG_OFF"));
5851 static MODCMD_FUNC(chan_opt_nodelete
)
5853 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5855 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5859 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5862 static MODCMD_FUNC(chan_opt_dynlimit
)
5864 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5867 static MODCMD_FUNC(chan_opt_offchannel
)
5869 struct chanData
*cData
= channel
->channel_info
;
5874 /* Set flag according to value. */
5875 if(enabled_string(argv
[1]))
5877 if(!IsOffChannel(cData
))
5878 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5879 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5882 else if(disabled_string(argv
[1]))
5884 if(IsOffChannel(cData
))
5886 struct mod_chanmode change
;
5887 mod_chanmode_init(&change
);
5889 change
.args
[0].mode
= MODE_CHANOP
;
5890 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5891 mod_chanmode_announce(chanserv
, channel
, &change
);
5893 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5898 reply("MSG_INVALID_BINARY", argv
[1]);
5904 /* Find current option value. */
5905 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5909 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5911 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5915 static MODCMD_FUNC(chan_opt_defaults
)
5917 struct userData
*uData
;
5918 struct chanData
*cData
;
5919 const char *confirm
;
5920 enum levelOption lvlOpt
;
5921 enum charOption chOpt
;
5923 cData
= channel
->channel_info
;
5924 uData
= GetChannelUser(cData
, user
->handle_info
);
5925 if(!uData
|| (uData
->access
< UL_OWNER
))
5927 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5930 confirm
= make_confirmation_string(uData
);
5931 if((argc
< 2) || strcmp(argv
[1], confirm
))
5933 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5936 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5937 cData
->modes
= chanserv_conf
.default_modes
;
5938 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5939 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5940 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5941 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5942 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5947 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5949 struct chanData
*cData
= channel
->channel_info
;
5950 struct userData
*uData
;
5951 unsigned short value
;
5955 if(!check_user_level(channel
, user
, option
, 1, 1))
5957 reply("CSMSG_CANNOT_SET");
5960 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5961 if(!value
&& strcmp(argv
[1], "0"))
5963 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5966 uData
= GetChannelUser(cData
, user
->handle_info
);
5967 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5969 reply("CSMSG_BAD_SETLEVEL");
5975 /* This test only applies to owners, since non-owners
5976 * trying to set an option to above their level get caught
5977 * by the CSMSG_BAD_SETLEVEL test above.
5979 if(value
> uData
->access
)
5981 reply("CSMSG_BAD_SETTERS");
5988 cData
->lvlOpts
[option
] = value
;
5990 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5994 static MODCMD_FUNC(chan_opt_enfops
)
5996 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5999 static MODCMD_FUNC(chan_opt_enfhalfops
)
6001 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
6003 static MODCMD_FUNC(chan_opt_enfmodes
)
6005 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6008 static MODCMD_FUNC(chan_opt_enftopic
)
6010 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6013 static MODCMD_FUNC(chan_opt_pubcmd
)
6015 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6018 static MODCMD_FUNC(chan_opt_setters
)
6020 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6023 static MODCMD_FUNC(chan_opt_userinfo
)
6025 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6028 static MODCMD_FUNC(chan_opt_topicsnarf
)
6030 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6033 static MODCMD_FUNC(chan_opt_inviteme
)
6035 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6038 /* TODO: Make look like this when no args are
6040 * -X3- -------------------------------
6041 * -X3- BanTimeout: Bans are removed:
6042 * -X3- ----- * indicates current -----
6043 * -X3- 0: [*] Never.
6044 * -X3- 1: [ ] After 10 minutes.
6045 * -X3- 2: [ ] After 2 hours.
6046 * -X3- 3: [ ] After 4 hours.
6047 * -X3- 4: [ ] After 24 hours.
6048 * -X3- 5: [ ] After one week.
6049 * -X3- ------------- End -------------
6052 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6054 struct chanData
*cData
= channel
->channel_info
;
6055 int count
= charOptions
[option
].count
, index
;
6059 index
= atoi(argv
[1]);
6061 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6063 reply("CSMSG_INVALID_NUMERIC", index
);
6064 /* Show possible values. */
6065 for(index
= 0; index
< count
; index
++)
6066 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6070 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6074 /* Find current option value. */
6077 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6081 /* Somehow, the option value is corrupt; reset it to the default. */
6082 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6087 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6091 static MODCMD_FUNC(chan_opt_automode
)
6093 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6096 static MODCMD_FUNC(chan_opt_protect
)
6098 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6101 static MODCMD_FUNC(chan_opt_toys
)
6103 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6106 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6108 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6111 static MODCMD_FUNC(chan_opt_bantimeout
)
6113 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6116 static MODCMD_FUNC(chan_opt_topicrefresh
)
6118 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6121 static MODCMD_FUNC(chan_opt_resync
)
6123 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6126 static struct svccmd_list set_shows_list
;
6129 handle_svccmd_unbind(struct svccmd
*target
) {
6131 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6132 if(target
== set_shows_list
.list
[ii
])
6133 set_shows_list
.used
= 0;
6136 static CHANSERV_FUNC(cmd_set
)
6138 struct svccmd
*subcmd
;
6142 /* Check if we need to (re-)initialize set_shows_list. */
6143 if(!set_shows_list
.used
)
6145 if(!set_shows_list
.size
)
6147 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6148 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6150 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6152 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6153 sprintf(buf
, "%s %s", argv
[0], name
);
6154 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6157 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6160 svccmd_list_append(&set_shows_list
, subcmd
);
6166 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6167 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6169 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6171 subcmd
= set_shows_list
.list
[ii
];
6172 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6174 reply("CSMSG_CHANNEL_OPTIONS_END");
6178 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6179 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6182 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6185 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6187 reply("CSMSG_NO_ACCESS");
6191 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6195 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6197 struct userData
*uData
;
6199 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6202 reply("CSMSG_NOT_USER", channel
->name
);
6208 /* Just show current option value. */
6210 else if(enabled_string(argv
[1]))
6212 uData
->flags
|= mask
;
6214 else if(disabled_string(argv
[1]))
6216 uData
->flags
&= ~mask
;
6220 reply("MSG_INVALID_BINARY", argv
[1]);
6224 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6228 static MODCMD_FUNC(user_opt_autoop
)
6230 struct userData
*uData
;
6232 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6235 reply("CSMSG_NOT_USER", channel
->name
);
6238 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6239 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6241 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6242 /* TODO: add halfops error message? or is the op one generic enough? */
6245 static MODCMD_FUNC(user_opt_autoinvite
)
6247 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6250 static MODCMD_FUNC(user_opt_autojoin
)
6252 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN
, CSFUNC_ARGS
);
6255 static MODCMD_FUNC(user_opt_info
)
6257 struct userData
*uData
;
6260 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6264 /* If they got past the command restrictions (which require access)
6265 * but fail this test, we have some fool with security override on.
6267 reply("CSMSG_NOT_USER", channel
->name
);
6274 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6275 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6277 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6280 bp
= strcspn(infoline
, "\001");
6283 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6288 if(infoline
[0] == '*' && infoline
[1] == 0)
6291 uData
->info
= strdup(infoline
);
6294 reply("CSMSG_USET_INFO", uData
->info
);
6296 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6300 struct svccmd_list uset_shows_list
;
6302 static CHANSERV_FUNC(cmd_uset
)
6304 struct svccmd
*subcmd
;
6308 /* Check if we need to (re-)initialize uset_shows_list. */
6309 if(!uset_shows_list
.used
)
6313 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6316 if(!uset_shows_list
.size
)
6318 uset_shows_list
.size
= ArrayLength(options
);
6319 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6321 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6323 const char *name
= options
[ii
];
6324 sprintf(buf
, "%s %s", argv
[0], name
);
6325 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6328 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6331 svccmd_list_append(&uset_shows_list
, subcmd
);
6337 /* Do this so options are presented in a consistent order. */
6338 reply("CSMSG_USER_OPTIONS");
6339 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6340 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6344 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6345 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6348 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6352 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6355 static CHANSERV_FUNC(cmd_giveownership
)
6357 struct handle_info
*new_owner_hi
;
6358 struct userData
*new_owner
, *curr_user
;
6359 struct chanData
*cData
= channel
->channel_info
;
6360 struct do_not_register
*dnr
;
6361 struct giveownership
*giveownership
;
6362 unsigned int force
, override
;
6363 unsigned short co_access
, new_owner_old_access
;
6364 char transfer_reason
[MAXLEN
];
6367 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6368 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6370 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6371 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6372 && (uData
->access
> 500)
6373 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6374 || uData
->access
< 500));
6377 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6379 struct userData
*owner
= NULL
;
6380 for(curr_user
= channel
->channel_info
->users
;
6382 curr_user
= curr_user
->next
)
6384 if(curr_user
->access
!= UL_OWNER
)
6388 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6395 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6397 char delay
[INTERVALLEN
];
6398 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6399 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6403 reply("CSMSG_NO_OWNER", channel
->name
);
6406 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6408 if(new_owner_hi
== user
->handle_info
)
6410 reply("CSMSG_NO_TRANSFER_SELF");
6413 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6418 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6422 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6426 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6428 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6431 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6432 if(!IsHelping(user
))
6433 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6435 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6439 new_owner_old_access
= new_owner
->access
;
6440 if(new_owner
->access
>= UL_COOWNER
)
6441 co_access
= new_owner
->access
;
6443 co_access
= UL_COOWNER
;
6444 new_owner
->access
= UL_OWNER
;
6446 curr_user
->access
= co_access
;
6447 cData
->ownerTransfer
= now
;
6449 giveownership
= calloc(1, sizeof(*giveownership
));
6450 giveownership
->issued
= now
;
6451 giveownership
->old_owner
= curr_user
->handle
->handle
;
6452 giveownership
->target
= new_owner_hi
->handle
;
6453 giveownership
->target_access
= new_owner_old_access
;
6456 if(argc
> (2 + force
))
6458 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6459 giveownership
->reason
= strdup(transfer_reason
);
6461 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6464 giveownership
->previous
= channel
->channel_info
->giveownership
;
6465 channel
->channel_info
->giveownership
= giveownership
;
6467 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6468 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6469 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6474 chanserv_expire_user_suspension(void *data
)
6476 struct userData
*target
= data
;
6478 target
->expires
= 0;
6479 target
->flags
&= ~USER_SUSPENDED
;
6482 static CHANSERV_FUNC(cmd_suspend
)
6484 struct handle_info
*hi
;
6485 struct userData
*self
, *target
;
6489 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6490 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6491 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6493 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6496 if(target
->access
>= self
->access
)
6498 reply("MSG_USER_OUTRANKED", hi
->handle
);
6501 if(target
->flags
& USER_SUSPENDED
)
6503 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6508 target
->present
= 0;
6511 if(!strcmp(argv
[2], "0"))
6515 unsigned int duration
;
6516 if(!(duration
= ParseInterval(argv
[2])))
6518 reply("MSG_INVALID_DURATION", argv
[2]);
6521 expiry
= now
+ duration
;
6524 target
->expires
= expiry
;
6527 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6529 target
->flags
|= USER_SUSPENDED
;
6530 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6534 static CHANSERV_FUNC(cmd_unsuspend
)
6536 struct handle_info
*hi
;
6537 struct userData
*self
, *target
;
6540 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6541 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6542 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6544 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6547 if(target
->access
>= self
->access
)
6549 reply("MSG_USER_OUTRANKED", hi
->handle
);
6552 if(!(target
->flags
& USER_SUSPENDED
))
6554 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6557 target
->flags
&= ~USER_SUSPENDED
;
6558 scan_user_presence(target
, NULL
);
6559 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6560 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6564 static MODCMD_FUNC(cmd_deleteme
)
6566 struct handle_info
*hi
;
6567 struct userData
*target
;
6568 const char *confirm_string
;
6569 unsigned short access
;
6572 hi
= user
->handle_info
;
6573 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6575 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6578 if(target
->access
== UL_OWNER
)
6580 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6583 confirm_string
= make_confirmation_string(target
);
6584 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6586 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6589 access
= target
->access
;
6590 channel_name
= strdup(channel
->name
);
6591 del_channel_user(target
, 1);
6592 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6598 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6600 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6601 struct chanData
*cData
;
6604 for(cData
= channelList
; cData
; cData
= cData
->next
)
6606 if(IsSuspended(cData
))
6608 opt
= cData
->chOpts
[chTopicRefresh
];
6611 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6614 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6615 cData
->last_refresh
= refresh_num
;
6617 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6621 chanserv_auto_resync(UNUSED_ARG(void *data
))
6623 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6624 struct chanData
*cData
;
6627 for(cData
= channelList
; cData
; cData
= cData
->next
)
6629 if(IsSuspended(cData
)) continue;
6630 opt
= cData
->chOpts
[chResync
];
6631 if(opt
== 'n') continue;
6632 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6633 resync_channel(cData
->channel
);
6634 cData
->last_resync
= refresh_num
;
6636 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6639 static CHANSERV_FUNC(cmd_unf
)
6643 char response
[MAXLEN
];
6644 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6645 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6646 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6649 reply("CSMSG_UNF_RESPONSE");
6653 static CHANSERV_FUNC(cmd_ping
)
6657 char response
[MAXLEN
];
6658 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6659 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6660 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6663 reply("CSMSG_PING_RESPONSE");
6667 static CHANSERV_FUNC(cmd_wut
)
6671 char response
[MAXLEN
];
6672 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6673 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6674 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6677 reply("CSMSG_WUT_RESPONSE");
6682 static CHANSERV_FUNC(cmd_8ball
)
6684 unsigned int i
, j
, accum
;
6689 for(i
=1; i
<argc
; i
++)
6690 for(j
=0; argv
[i
][j
]; j
++)
6691 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6692 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6695 char response
[MAXLEN
];
6696 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6697 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6700 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6704 #else /* Use cool 8ball instead */
6706 void eightball(char *outcome
, int method
, unsigned int seed
)
6710 #define NUMOFCOLORS 18
6711 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6712 "white", "black", "grey", "brown",
6713 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6714 "fuchsia","turquoise","magenta", "cyan"};
6715 #define NUMOFLOCATIONS 50
6716 char balllocations
[50][55] = {
6717 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6718 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6719 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6720 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6721 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6722 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6723 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6724 "your bra", "your hair", "your bed", "the couch", "the wall",
6725 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6726 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6727 #define NUMOFPREPS 15
6728 char ballpreps
[50][50] = {
6729 "Near", "Somewhere near", "In", "In", "In",
6730 "In", "Hiding in", "Under", "Next to", "Over",
6731 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6732 #define NUMOFNUMS 34
6733 char ballnums
[50][50] = {
6734 "A hundred", "A thousand", "A few", "42",
6735 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6736 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6737 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6739 #define NUMOFMULTS 8
6740 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6743 * 0: normal (Not used in x3)
6750 if (method
== 1) /* A Color */
6754 answer
= (rand() % 12); /* Make sure this is the # of entries */
6757 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6759 case 1: strcpy(tmp
, "Sort of a light %s color.");
6761 case 2: strcpy(tmp
, "Dark and dreary %s.");
6763 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6765 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6767 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6769 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6771 case 10: strcpy(tmp
, "Solid %s.");
6773 case 11: strcpy(tmp
, "Transparent %s.");
6775 default: strcpy(outcome
, "An invalid random number was generated.");
6778 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6781 else if (method
== 2) /* Location */
6783 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6785 else if (method
== 3) /* Number of ___ */
6787 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6791 //Debug(DBGWARNING, "Error in 8ball.");
6796 static CHANSERV_FUNC(cmd_8ball
)
6798 char *word1
, *word2
, *word3
;
6799 static char eb
[MAXLEN
];
6800 unsigned int accum
, i
, j
;
6804 for(i
=1; i
<argc
; i
++)
6805 for(j
=0; argv
[i
][j
]; j
++)
6806 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6808 accum
+= time(NULL
)/3600;
6810 word2
= argc
>2?argv
[2]:"";
6811 word3
= argc
>3?argv
[3]:"";
6814 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6815 eightball(eb
, 1, accum
);
6816 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6817 eightball(eb
, 1, accum
);
6818 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6819 eightball(eb
, 1, accum
);
6820 /*** LOCATION *****/
6825 (strcasecmp(word1
, "where") == 0) &&
6826 (strcasecmp(word2
, "is") == 0)
6830 strcasecmp(word1
, "where's") == 0
6833 eightball(eb
, 2, accum
);
6835 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6836 eightball(eb
, 3, accum
);
6840 /* Generic 8ball question.. so pull from x3.conf srvx style */
6843 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6846 char response
[MAXLEN
];
6847 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6848 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6851 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6857 char response
[MAXLEN
];
6858 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6859 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6862 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6867 static CHANSERV_FUNC(cmd_d
)
6869 unsigned long sides
, count
, modifier
, ii
, total
;
6870 char response
[MAXLEN
], *sep
;
6874 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6884 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6885 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6889 else if((sep
[0] == '-') && isdigit(sep
[1]))
6890 modifier
= strtoul(sep
, NULL
, 10);
6891 else if((sep
[0] == '+') && isdigit(sep
[1]))
6892 modifier
= strtoul(sep
+1, NULL
, 10);
6899 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6904 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6907 for(total
= ii
= 0; ii
< count
; ++ii
)
6908 total
+= (rand() % sides
) + 1;
6911 if((count
> 1) || modifier
)
6913 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6914 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6918 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6919 sprintf(response
, fmt
, total
, sides
);
6922 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6924 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6928 static CHANSERV_FUNC(cmd_huggle
)
6930 /* CTCP must be via PRIVMSG, never notice */
6932 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6934 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6938 static CHANSERV_FUNC(cmd_calc
)
6940 char response
[MAXLEN
];
6943 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6946 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6948 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6952 static CHANSERV_FUNC(cmd_reply
)
6956 unsplit_string(argv
+ 1, argc
- 1, NULL
);
6959 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6961 send_message_type(4, user
, cmd
->parent
->bot
, "%s", unsplit_string(argv
+ 1, argc
- 1, NULL
));
6966 chanserv_adjust_limit(void *data
)
6968 struct mod_chanmode change
;
6969 struct chanData
*cData
= data
;
6970 struct chanNode
*channel
= cData
->channel
;
6973 if(IsSuspended(cData
))
6976 cData
->limitAdjusted
= now
;
6977 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6978 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6980 if(limit
> cData
->modes
.new_limit
)
6981 limit
= cData
->modes
.new_limit
;
6982 else if(limit
== cData
->modes
.new_limit
)
6986 mod_chanmode_init(&change
);
6987 change
.modes_set
= MODE_LIMIT
;
6988 change
.new_limit
= limit
;
6989 mod_chanmode_announce(chanserv
, channel
, &change
);
6993 handle_new_channel(struct chanNode
*channel
)
6995 struct chanData
*cData
;
6997 if(!(cData
= channel
->channel_info
))
7000 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
7001 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
7003 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
7004 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
7007 /* Welcome to my worst nightmare. Warning: Read (or modify)
7008 the code below at your own risk. */
7010 handle_join(struct modeNode
*mNode
)
7012 struct mod_chanmode change
;
7013 struct userNode
*user
= mNode
->user
;
7014 struct chanNode
*channel
= mNode
->channel
;
7015 struct chanData
*cData
;
7016 struct userData
*uData
= NULL
;
7017 struct banData
*bData
;
7018 struct handle_info
*handle
;
7019 unsigned int modes
= 0, info
= 0;
7022 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7025 cData
= channel
->channel_info
;
7026 if(channel
->members
.used
> cData
->max
)
7027 cData
->max
= channel
->members
.used
;
7030 /* Check for bans. If they're joining through a ban, one of two
7032 * 1: Join during a netburst, by riding the break. Kick them
7033 * unless they have ops or voice in the channel.
7034 * 2: They're allowed to join through the ban (an invite in
7035 * ircu2.10, or a +e on Hybrid, or something).
7036 * If they're not joining through a ban, and the banlist is not
7037 * full, see if they're on the banlist for the channel. If so,
7040 if(user
->uplink
->burst
&& !mNode
->modes
)
7043 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7045 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7047 /* Riding a netburst. Naughty. */
7048 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7055 mod_chanmode_init(&change
);
7057 if(channel
->banlist
.used
< MAXBANS
)
7059 /* Not joining through a ban. */
7060 for(bData
= cData
->bans
;
7061 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7062 bData
= bData
->next
);
7066 char kick_reason
[MAXLEN
];
7067 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7069 bData
->triggered
= now
;
7070 if(bData
!= cData
->bans
)
7072 /* Shuffle the ban to the head of the list. */
7074 bData
->next
->prev
= bData
->prev
;
7076 bData
->prev
->next
= bData
->next
;
7079 bData
->next
= cData
->bans
;
7082 cData
->bans
->prev
= bData
;
7083 cData
->bans
= bData
;
7086 change
.args
[0].mode
= MODE_BAN
;
7087 change
.args
[0].u
.hostmask
= bData
->mask
;
7088 mod_chanmode_announce(chanserv
, channel
, &change
);
7089 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7094 /* ChanServ will not modify the limits in join-flooded channels.
7095 It will also skip DynLimit processing when the user (or srvx)
7096 is bursting in, because there are likely more incoming. */
7097 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7098 && !user
->uplink
->burst
7099 && !channel
->join_flooded
7100 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7102 /* The user count has begun "bumping" into the channel limit,
7103 so set a timer to raise the limit a bit. Any previous
7104 timers are removed so three incoming users within the delay
7105 results in one limit change, not three. */
7107 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7108 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7111 /* Give automodes exept during join-floods */
7112 if(!channel
->join_flooded
)
7114 if(cData
->chOpts
[chAutomode
] == 'v')
7115 modes
|= MODE_VOICE
;
7116 else if(cData
->chOpts
[chAutomode
] == 'h')
7117 modes
|= MODE_HALFOP
;
7118 else if(cData
->chOpts
[chAutomode
] == 'o')
7119 modes
|= MODE_CHANOP
;
7122 greeting
= cData
->greeting
;
7123 if(user
->handle_info
)
7125 handle
= user
->handle_info
;
7127 if(IsHelper(user
) && !IsHelping(user
))
7130 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7132 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7134 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7140 uData
= GetTrueChannelAccess(cData
, handle
);
7141 if(uData
&& !IsUserSuspended(uData
))
7143 /* non users getting automodes are handled above. */
7144 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7146 /* just op everyone with access */
7147 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7148 modes
|= MODE_VOICE
;
7149 /* or do their access level */
7150 else if(uData
->access
>= UL_OP
)
7151 modes
|= MODE_CHANOP
;
7152 else if(uData
->access
>= UL_HALFOP
)
7153 modes
|= MODE_HALFOP
;
7154 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7155 modes
|= MODE_VOICE
;
7157 if(uData
->access
>= UL_PRESENT
)
7158 cData
->visited
= now
;
7159 if(cData
->user_greeting
)
7160 greeting
= cData
->user_greeting
;
7162 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7163 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7171 /* If user joining normally (not during burst), apply op or voice,
7172 * and send greeting/userinfo as appropriate.
7174 if(!user
->uplink
->burst
)
7178 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7179 if(modes & MODE_CHANOP) {
7180 modes &= ~MODE_HALFOP;
7181 modes &= ~MODE_VOICE;
7184 change
.args
[0].mode
= modes
;
7185 change
.args
[0].u
.member
= mNode
;
7186 mod_chanmode_announce(chanserv
, channel
, &change
);
7189 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7191 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7197 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7199 struct mod_chanmode change
;
7200 struct userData
*channel
;
7201 unsigned int ii
, jj
, i
;
7203 if(!user
->handle_info
)
7206 mod_chanmode_init(&change
);
7208 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7210 struct chanNode
*cn
;
7211 struct modeNode
*mn
;
7212 if(IsUserSuspended(channel
)
7213 || IsSuspended(channel
->channel
)
7214 || !(cn
= channel
->channel
->channel
))
7217 mn
= GetUserMode(cn
, user
);
7220 if(!IsUserSuspended(channel
)
7221 && IsUserAutoInvite(channel
)
7222 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7224 && !user
->uplink
->burst
)
7225 irc_invite(chanserv
, user
, cn
);
7227 if(!IsUserSuspended(channel
)
7228 && IsUserAutoJoin(channel
)
7229 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7231 && !user
->uplink
->burst
)
7232 irc_svsjoin(chanserv
, user
, cn
);
7237 if(channel
->access
>= UL_PRESENT
)
7238 channel
->channel
->visited
= now
;
7240 if(IsUserAutoOp(channel
))
7242 if(channel
->access
>= UL_OP
)
7243 change
.args
[0].mode
= MODE_CHANOP
;
7244 else if(channel
->access
>= UL_HALFOP
)
7245 change
.args
[0].mode
= MODE_HALFOP
;
7246 else if(channel
->access
>= UL_PEON
)
7247 change
.args
[0].mode
= MODE_VOICE
;
7249 change
.args
[0].mode
= 0;
7250 change
.args
[0].u
.member
= mn
;
7251 if(change
.args
[0].mode
)
7252 mod_chanmode_announce(chanserv
, cn
, &change
);
7255 channel
->seen
= now
;
7256 channel
->present
= 1;
7259 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7261 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7262 struct banData
*ban
;
7264 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7265 || !channel
->channel_info
7266 || IsSuspended(channel
->channel_info
))
7268 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7269 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7271 if(jj
< channel
->banlist
.used
)
7273 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7275 char kick_reason
[MAXLEN
];
7276 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7278 change
.args
[0].mode
= MODE_BAN
;
7279 change
.args
[0].u
.hostmask
= ban
->mask
;
7280 mod_chanmode_announce(chanserv
, channel
, &change
);
7281 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7282 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7283 ban
->triggered
= now
;
7288 if(IsSupportHelper(user
))
7290 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7292 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7294 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7300 if (user
->handle_info
->ignores
->used
) {
7301 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7302 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7306 if (user
->handle_info
->epithet
)
7307 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7311 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7313 struct chanData
*cData
;
7314 struct userData
*uData
;
7316 cData
= mn
->channel
->channel_info
;
7317 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7320 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7322 /* Allow for a bit of padding so that the limit doesn't
7323 track the user count exactly, which could get annoying. */
7324 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7326 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7327 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7331 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7333 scan_user_presence(uData
, mn
->user
);
7337 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7339 unsigned int ii
, jj
;
7340 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7342 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7343 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7345 if(jj
< mn
->user
->channels
.used
)
7348 if(ii
== chanserv_conf
.support_channels
.used
)
7349 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7354 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7356 struct userData
*uData
;
7358 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7359 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7360 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7363 if(protect_user(victim
, kicker
, channel
->channel_info
))
7365 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7366 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7369 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7374 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7376 struct chanData
*cData
;
7378 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7381 cData
= channel
->channel_info
;
7382 if(bad_topic(channel
, user
, channel
->topic
))
7383 { /* User doesnt have privs to set topics. Undo it */
7384 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7385 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7388 /* If there is a topic mask set, and the new topic doesnt match,
7389 * set the topic to mask + new_topic */
7390 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7392 char new_topic
[TOPICLEN
+1];
7393 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7396 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7397 /* and fall through to topicsnarf code below.. */
7399 else /* Topic couldnt fit into mask, was too long */
7401 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7402 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7403 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7407 /* With topicsnarf, grab the topic and save it as the default topic. */
7408 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7411 cData
->topic
= strdup(channel
->topic
);
7417 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7419 struct mod_chanmode
*bounce
= NULL
;
7420 unsigned int bnc
, ii
;
7423 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7426 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7427 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7429 char correct
[MAXLEN
];
7430 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7431 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7432 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7434 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7436 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7438 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7439 if(!protect_user(victim
, user
, channel
->channel_info
))
7442 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7445 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7446 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7447 if(bounce
->args
[bnc
].u
.member
)
7451 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7452 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7454 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7456 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7458 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7459 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7462 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7463 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7464 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7467 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7469 const char *ban
= change
->args
[ii
].u
.hostmask
;
7470 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7473 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7474 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7475 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7477 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7482 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7483 mod_chanmode_announce(chanserv
, channel
, bounce
);
7484 for(ii
= 0; ii
< change
->argc
; ++ii
)
7485 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7486 free((char*)bounce
->args
[ii
].u
.hostmask
);
7487 mod_chanmode_free(bounce
);
7492 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7494 struct chanNode
*channel
;
7495 struct banData
*bData
;
7496 struct mod_chanmode change
;
7497 unsigned int ii
, jj
;
7498 char kick_reason
[MAXLEN
];
7500 mod_chanmode_init(&change
);
7502 change
.args
[0].mode
= MODE_BAN
;
7503 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7505 channel
= user
->channels
.list
[ii
]->channel
;
7506 /* Need not check for bans if they're opped or voiced. */
7507 /* TODO: does this make sense in automode v, h, and o? *
7508 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7509 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7511 /* Need not check for bans unless channel registration is active. */
7512 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7514 /* Look for a matching ban already on the channel. */
7515 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7516 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7518 /* Need not act if we found one. */
7519 if(jj
< channel
->banlist
.used
)
7521 /* Look for a matching ban in this channel. */
7522 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7524 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7526 change
.args
[0].u
.hostmask
= bData
->mask
;
7527 mod_chanmode_announce(chanserv
, channel
, &change
);
7528 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7529 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7530 bData
->triggered
= now
;
7531 break; /* we don't need to check any more bans in the channel */
7536 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7538 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7542 dict_remove2(handle_dnrs
, old_handle
, 1);
7543 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7544 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7549 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7551 struct userNode
*h_user
;
7553 if(handle
->channels
)
7555 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7556 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7558 while(handle
->channels
)
7559 del_channel_user(handle
->channels
, 1);
7564 handle_server_link(UNUSED_ARG(struct server
*server
))
7566 struct chanData
*cData
;
7568 for(cData
= channelList
; cData
; cData
= cData
->next
)
7570 if(!IsSuspended(cData
))
7571 cData
->may_opchan
= 1;
7572 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7573 && !cData
->channel
->join_flooded
7574 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7575 < chanserv_conf
.adjust_threshold
))
7577 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7578 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7584 chanserv_conf_read(void)
7588 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7589 struct mod_chanmode
*change
;
7590 struct string_list
*strlist
;
7591 struct chanNode
*chan
;
7594 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7596 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7599 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7600 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7601 chanserv_conf
.support_channels
.used
= 0;
7602 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7604 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7606 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7609 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7611 channelList_append(&chanserv_conf
.support_channels
, chan
);
7614 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7617 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7620 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7622 channelList_append(&chanserv_conf
.support_channels
, chan
);
7624 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7625 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7626 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7627 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7628 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7629 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7630 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7631 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7632 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7633 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7634 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7635 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7636 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7637 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7638 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7639 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7640 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7641 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7642 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7643 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7644 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7645 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7646 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7647 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7648 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7650 NickChange(chanserv
, str
, 0);
7651 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7652 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7653 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7654 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7655 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7656 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7657 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7658 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7659 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7660 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7661 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7662 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7663 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7664 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7665 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7666 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7667 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7668 god_timeout
= str
? ParseInterval(str
) : 60*15;
7669 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7672 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7673 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7674 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7675 && (change
->argc
< 2))
7677 chanserv_conf
.default_modes
= *change
;
7678 mod_chanmode_free(change
);
7680 free_string_list(chanserv_conf
.set_shows
);
7681 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7683 strlist
= string_list_copy(strlist
);
7686 static const char *list
[] = {
7687 /* free form text */
7688 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7689 /* options based on user level */
7690 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7691 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7692 /* multiple choice options */
7693 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7694 /* binary options */
7695 "DynLimit", "NoDelete", "BanTimeout",
7700 strlist
= alloc_string_list(ArrayLength(list
)-1);
7701 for(ii
=0; list
[ii
]; ii
++)
7702 string_list_append(strlist
, strdup(list
[ii
]));
7704 chanserv_conf
.set_shows
= strlist
;
7705 /* We don't look things up now, in case the list refers to options
7706 * defined by modules initialized after this point. Just mark the
7707 * function list as invalid, so it will be initialized.
7709 set_shows_list
.used
= 0;
7710 free_string_list(chanserv_conf
.eightball
);
7711 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7714 strlist
= string_list_copy(strlist
);
7718 strlist
= alloc_string_list(4);
7719 string_list_append(strlist
, strdup("Yes."));
7720 string_list_append(strlist
, strdup("No."));
7721 string_list_append(strlist
, strdup("Maybe so."));
7723 chanserv_conf
.eightball
= strlist
;
7724 free_string_list(chanserv_conf
.old_ban_names
);
7725 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7727 strlist
= string_list_copy(strlist
);
7729 strlist
= alloc_string_list(2);
7730 chanserv_conf
.old_ban_names
= strlist
;
7731 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7732 off_channel
= str
? atoi(str
) : 0;
7736 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7739 struct note_type
*ntype
;
7742 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7744 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7747 if(!(ntype
= chanserv_create_note_type(key
)))
7749 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7753 /* Figure out set access */
7754 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7756 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7757 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7759 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7761 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7762 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7764 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7766 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7770 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7771 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7772 ntype
->set_access
.min_opserv
= 0;
7775 /* Figure out visibility */
7776 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7777 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7778 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7779 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7780 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7781 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7782 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7783 ntype
->visible_type
= NOTE_VIS_ALL
;
7785 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7787 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7788 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7792 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7794 struct handle_info
*handle
;
7795 struct userData
*uData
;
7796 char *seen
, *inf
, *flags
, *expires
;
7798 unsigned short access
;
7800 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7802 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7806 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7807 if(access
> UL_OWNER
)
7809 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7813 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7814 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7815 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7816 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7817 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7818 handle
= get_handle_info(key
);
7821 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7825 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7826 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7827 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7829 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7831 if(uData
->expires
> now
)
7832 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7834 uData
->flags
&= ~USER_SUSPENDED
;
7837 /* Upgrade: set autoop to the inverse of noautoop */
7838 if(chanserv_read_version
< 2)
7840 /* if noautoop is true, set autoop false, and vice versa */
7841 if(uData
->flags
& USER_NOAUTO_OP
)
7842 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7844 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7845 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
);
7851 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7853 struct banData
*bData
;
7854 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7855 time_t set_time
, triggered_time
, expires_time
;
7857 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7859 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7863 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7864 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7865 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7866 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7867 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7868 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7869 if (!reason
|| !owner
)
7872 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7873 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7875 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7877 expires_time
= set_time
+ atoi(s_duration
);
7881 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7884 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7887 static struct suspended
*
7888 chanserv_read_suspended(dict_t obj
)
7890 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7894 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7895 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7896 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7897 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7898 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7899 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7900 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7901 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7902 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7903 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7907 static struct giveownership
*
7908 chanserv_read_giveownership(dict_t obj
)
7910 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7914 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7915 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7917 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7919 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7920 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7922 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7923 giveownership
->reason
= str
? strdup(str
) : NULL
;
7924 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7925 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7927 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7928 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7929 return giveownership
;
7933 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7935 struct suspended
*suspended
;
7936 struct giveownership
*giveownership
;
7937 struct mod_chanmode
*modes
;
7938 struct chanNode
*cNode
;
7939 struct chanData
*cData
;
7940 struct dict
*channel
, *obj
;
7941 char *str
, *argv
[10];
7945 channel
= hir
->d
.object
;
7947 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7950 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7953 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7956 cData
= register_channel(cNode
, str
);
7959 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7963 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7965 enum levelOption lvlOpt
;
7966 enum charOption chOpt
;
7968 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7969 cData
->flags
= atoi(str
);
7971 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7973 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7975 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7976 else if(levelOptions
[lvlOpt
].old_flag
)
7978 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7979 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7981 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7985 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7987 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7989 cData
->chOpts
[chOpt
] = str
[0];
7992 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7994 enum levelOption lvlOpt
;
7995 enum charOption chOpt
;
7998 cData
->flags
= base64toint(str
, 5);
7999 count
= strlen(str
+= 5);
8000 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8003 if(levelOptions
[lvlOpt
].old_flag
)
8005 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
8006 lvl
= levelOptions
[lvlOpt
].flag_value
;
8008 lvl
= levelOptions
[lvlOpt
].default_value
;
8010 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
8012 case 'c': lvl
= UL_COOWNER
; break;
8013 case 'm': lvl
= UL_MANAGER
; break;
8014 case 'n': lvl
= UL_OWNER
+1; break;
8015 case 'o': lvl
= UL_OP
; break;
8016 case 'p': lvl
= UL_PEON
; break;
8017 case 'h': lvl
= UL_HALFOP
; break;
8018 case 'w': lvl
= UL_OWNER
; break;
8019 default: lvl
= 0; break;
8021 cData
->lvlOpts
[lvlOpt
] = lvl
;
8023 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8024 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
8027 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
8029 suspended
= chanserv_read_suspended(obj
);
8030 cData
->suspended
= suspended
;
8031 suspended
->cData
= cData
;
8032 /* We could use suspended->expires and suspended->revoked to
8033 * set the CHANNEL_SUSPENDED flag, but we don't. */
8035 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8037 suspended
= calloc(1, sizeof(*suspended
));
8038 suspended
->issued
= 0;
8039 suspended
->revoked
= 0;
8040 suspended
->suspender
= strdup(str
);
8041 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8042 suspended
->expires
= str
? atoi(str
) : 0;
8043 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8044 suspended
->reason
= strdup(str
? str
: "No reason");
8045 suspended
->previous
= NULL
;
8046 cData
->suspended
= suspended
;
8047 suspended
->cData
= cData
;
8051 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8052 suspended
= NULL
; /* to squelch a warning */
8055 if(IsSuspended(cData
)) {
8056 if(suspended
->expires
> now
)
8057 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8058 else if(suspended
->expires
)
8059 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8062 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8064 giveownership
= chanserv_read_giveownership(obj
);
8065 cData
->giveownership
= giveownership
;
8068 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8069 struct mod_chanmode change
;
8070 mod_chanmode_init(&change
);
8072 change
.args
[0].mode
= MODE_CHANOP
;
8073 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8074 mod_chanmode_announce(chanserv
, cNode
, &change
);
8077 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8078 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8079 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8080 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8081 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8082 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8083 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8084 cData
->max
= str
? atoi(str
) : 0;
8085 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8086 cData
->greeting
= str
? strdup(str
) : NULL
;
8087 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8088 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8089 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8090 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8091 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8092 cData
->topic
= str
? strdup(str
) : NULL
;
8094 if(!IsSuspended(cData
)
8095 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8096 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8097 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8098 cData
->modes
= *modes
;
8100 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8101 if(cData
->modes
.argc
> 1)
8102 cData
->modes
.argc
= 1;
8103 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8104 mod_chanmode_free(modes
);
8107 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8108 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8109 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8111 if(!cData
->users
&& !IsProtected(cData
))
8113 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8114 unregister_channel(cData
, "has empty user list.");
8118 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8119 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8120 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8122 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8123 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8125 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8126 struct record_data
*rd
= iter_data(it
);
8127 const char *note
, *setter
;
8129 if(rd
->type
!= RECDB_OBJECT
)
8131 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8135 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8137 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8139 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8143 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8144 if(!setter
) setter
= "<unknown>";
8145 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8153 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8155 const char *setter
, *reason
, *str
;
8156 struct do_not_register
*dnr
;
8158 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8161 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8164 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8167 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8170 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8173 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8175 dnr
->set
= atoi(str
);
8181 chanserv_version_read(struct dict
*section
)
8185 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8187 chanserv_read_version
= atoi(str
);
8188 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8192 chanserv_saxdb_read(struct dict
*database
)
8194 struct dict
*section
;
8197 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8198 chanserv_version_read(section
);
8200 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8201 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8202 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8204 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8205 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8206 chanserv_channel_read(iter_key(it
), iter_data(it
));
8208 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8209 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8210 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8216 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8218 int high_present
= 0;
8219 saxdb_start_record(ctx
, KEY_USERS
, 1);
8220 for(; uData
; uData
= uData
->next
)
8222 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8224 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8225 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8226 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8228 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8230 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8232 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8233 saxdb_end_record(ctx
);
8235 saxdb_end_record(ctx
);
8236 return high_present
;
8240 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8244 saxdb_start_record(ctx
, KEY_BANS
, 1);
8245 for(; bData
; bData
= bData
->next
)
8247 saxdb_start_record(ctx
, bData
->mask
, 0);
8248 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8249 if(bData
->triggered
)
8250 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8252 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8254 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8256 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8257 saxdb_end_record(ctx
);
8259 saxdb_end_record(ctx
);
8263 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8265 saxdb_start_record(ctx
, name
, 0);
8266 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8267 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8269 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8271 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8273 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8275 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8276 saxdb_end_record(ctx
);
8280 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8282 saxdb_start_record(ctx
, name
, 0);
8283 if(giveownership
->staff_issuer
)
8284 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8285 if(giveownership
->old_owner
)
8286 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8287 if(giveownership
->target
)
8288 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8289 if(giveownership
->target_access
)
8290 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8291 if(giveownership
->reason
)
8292 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8293 if(giveownership
->issued
)
8294 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8295 if(giveownership
->previous
)
8296 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8297 saxdb_end_record(ctx
);
8301 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8305 enum levelOption lvlOpt
;
8306 enum charOption chOpt
;
8308 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8310 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8311 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8313 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8314 if(channel
->registrar
)
8315 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8316 if(channel
->greeting
)
8317 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8318 if(channel
->user_greeting
)
8319 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8320 if(channel
->topic_mask
)
8321 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8322 if(channel
->suspended
)
8323 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8324 if(channel
->giveownership
)
8325 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8327 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8328 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8329 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8330 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8331 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8333 buf
[0] = channel
->chOpts
[chOpt
];
8335 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8337 saxdb_end_record(ctx
);
8339 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8341 mod_chanmode_format(&channel
->modes
, buf
);
8342 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8345 high_present
= chanserv_write_users(ctx
, channel
->users
);
8346 chanserv_write_bans(ctx
, channel
->bans
);
8348 if(dict_size(channel
->notes
))
8352 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8353 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8355 struct note
*note
= iter_data(it
);
8356 saxdb_start_record(ctx
, iter_key(it
), 0);
8357 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8358 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8359 saxdb_end_record(ctx
);
8361 saxdb_end_record(ctx
);
8364 if(channel
->ownerTransfer
)
8365 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8366 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8367 saxdb_end_record(ctx
);
8371 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8375 saxdb_start_record(ctx
, ntype
->name
, 0);
8376 switch(ntype
->set_access_type
)
8378 case NOTE_SET_CHANNEL_ACCESS
:
8379 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8381 case NOTE_SET_CHANNEL_SETTER
:
8382 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8384 case NOTE_SET_PRIVILEGED
: default:
8385 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8388 switch(ntype
->visible_type
)
8390 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8391 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8392 case NOTE_VIS_PRIVILEGED
:
8393 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8395 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8396 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8397 saxdb_end_record(ctx
);
8401 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8403 struct do_not_register
*dnr
;
8406 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8408 dnr
= iter_data(it
);
8409 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8411 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8412 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8413 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8414 saxdb_end_record(ctx
);
8419 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8422 struct chanData
*channel
;
8424 /* Version Control*/
8425 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8426 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8427 saxdb_end_record(ctx
);
8430 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8431 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8432 chanserv_write_note_type(ctx
, iter_data(it
));
8433 saxdb_end_record(ctx
);
8436 saxdb_start_record(ctx
, KEY_DNR
, 1);
8437 write_dnrs_helper(ctx
, handle_dnrs
);
8438 write_dnrs_helper(ctx
, plain_dnrs
);
8439 write_dnrs_helper(ctx
, mask_dnrs
);
8440 saxdb_end_record(ctx
);
8443 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8444 for(channel
= channelList
; channel
; channel
= channel
->next
)
8445 chanserv_write_channel(ctx
, channel
);
8446 saxdb_end_record(ctx
);
8452 chanserv_db_cleanup(void) {
8454 unreg_part_func(handle_part
);
8456 unregister_channel(channelList
, "terminating.");
8457 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8458 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8459 free(chanserv_conf
.support_channels
.list
);
8460 dict_delete(handle_dnrs
);
8461 dict_delete(plain_dnrs
);
8462 dict_delete(mask_dnrs
);
8463 dict_delete(note_types
);
8464 free_string_list(chanserv_conf
.eightball
);
8465 free_string_list(chanserv_conf
.old_ban_names
);
8466 free_string_list(chanserv_conf
.set_shows
);
8467 free(set_shows_list
.list
);
8468 free(uset_shows_list
.list
);
8471 struct userData
*helper
= helperList
;
8472 helperList
= helperList
->next
;
8477 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8478 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8479 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8482 init_chanserv(const char *nick
)
8484 struct chanNode
*chan
;
8486 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8487 conf_register_reload(chanserv_conf_read
);
8489 reg_server_link_func(handle_server_link
);
8491 reg_new_channel_func(handle_new_channel
);
8492 reg_join_func(handle_join
);
8493 reg_part_func(handle_part
);
8494 reg_kick_func(handle_kick
);
8495 reg_topic_func(handle_topic
);
8496 reg_mode_change_func(handle_mode
);
8497 reg_nick_change_func(handle_nick_change
);
8499 reg_auth_func(handle_auth
);
8500 reg_handle_rename_func(handle_rename
);
8501 reg_unreg_func(handle_unreg
);
8503 handle_dnrs
= dict_new();
8504 dict_set_free_data(handle_dnrs
, free
);
8505 plain_dnrs
= dict_new();
8506 dict_set_free_data(plain_dnrs
, free
);
8507 mask_dnrs
= dict_new();
8508 dict_set_free_data(mask_dnrs
, free
);
8510 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8511 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8512 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8513 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8514 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8515 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8516 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8517 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8518 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8519 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8521 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8523 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8524 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8526 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8527 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8528 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8529 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8530 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8532 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8533 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8534 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8535 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8536 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8537 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8539 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8540 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8541 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8542 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8544 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8545 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8546 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8547 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8548 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8549 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8550 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8551 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8552 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8553 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8555 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8556 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8557 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8558 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8559 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8560 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8561 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8562 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8563 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8564 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8565 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8566 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8567 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8568 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8570 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8571 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8572 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8573 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8575 /* if you change dellamer access, see also places
8576 * like unbanme which have manager hardcoded. */
8577 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8578 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8580 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8582 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8584 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8585 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8586 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8587 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8588 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8589 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8590 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8591 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8592 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8593 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8594 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8595 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8597 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8598 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8600 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8601 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8602 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8603 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8605 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8606 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8607 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8608 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8609 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8611 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8612 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8613 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8614 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8615 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8616 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8617 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8618 DEFINE_COMMAND(reply
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8620 /* Channel options */
8621 DEFINE_CHANNEL_OPTION(defaulttopic
);
8622 DEFINE_CHANNEL_OPTION(topicmask
);
8623 DEFINE_CHANNEL_OPTION(greeting
);
8624 DEFINE_CHANNEL_OPTION(usergreeting
);
8625 DEFINE_CHANNEL_OPTION(modes
);
8626 DEFINE_CHANNEL_OPTION(enfops
);
8627 DEFINE_CHANNEL_OPTION(enfhalfops
);
8628 DEFINE_CHANNEL_OPTION(automode
);
8629 DEFINE_CHANNEL_OPTION(protect
);
8630 DEFINE_CHANNEL_OPTION(enfmodes
);
8631 DEFINE_CHANNEL_OPTION(enftopic
);
8632 DEFINE_CHANNEL_OPTION(pubcmd
);
8633 DEFINE_CHANNEL_OPTION(userinfo
);
8634 DEFINE_CHANNEL_OPTION(dynlimit
);
8635 DEFINE_CHANNEL_OPTION(topicsnarf
);
8636 DEFINE_CHANNEL_OPTION(nodelete
);
8637 DEFINE_CHANNEL_OPTION(toys
);
8638 DEFINE_CHANNEL_OPTION(setters
);
8639 DEFINE_CHANNEL_OPTION(topicrefresh
);
8640 DEFINE_CHANNEL_OPTION(resync
);
8641 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8642 DEFINE_CHANNEL_OPTION(bantimeout
);
8643 DEFINE_CHANNEL_OPTION(inviteme
);
8645 DEFINE_CHANNEL_OPTION(offchannel
);
8646 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8648 /* Alias set topic to set defaulttopic for compatibility. */
8649 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8652 DEFINE_USER_OPTION(autoinvite
);
8653 DEFINE_USER_OPTION(autojoin
);
8654 DEFINE_USER_OPTION(info
);
8655 DEFINE_USER_OPTION(autoop
);
8657 /* Alias uset autovoice to uset autoop. */
8658 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8660 note_types
= dict_new();
8661 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8664 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8665 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8666 service_register(chanserv
)->trigger
= '!';
8667 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8670 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8672 if(chanserv_conf
.channel_expire_frequency
)
8673 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8675 if(chanserv_conf
.ban_timeout_frequency
)
8676 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8678 if(chanserv_conf
.refresh_period
)
8680 time_t next_refresh
;
8681 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8682 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8683 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8686 if (autojoin_channels
&& chanserv
) {
8687 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8688 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8689 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8693 reg_exit_func(chanserv_db_cleanup
);
8694 message_register_table(msgtab
);