1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_VERSION_CONTROL "version_control"
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
65 /* version control paramiter */
66 #define KEY_VERSION_NUMBER "version_number"
68 /* Note type parameters */
69 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
70 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
71 #define KEY_NOTE_SETTER_ACCESS "setter_access"
72 #define KEY_NOTE_VISIBILITY "visibility"
73 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
74 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
75 #define KEY_NOTE_VIS_ALL "all"
76 #define KEY_NOTE_MAX_LENGTH "max_length"
77 #define KEY_NOTE_SETTER "setter"
78 #define KEY_NOTE_NOTE "note"
80 /* Do-not-register channels */
82 #define KEY_DNR_SET "set"
83 #define KEY_DNR_SETTER "setter"
84 #define KEY_DNR_REASON "reason"
87 #define KEY_REGISTERED "registered"
88 #define KEY_REGISTRAR "registrar"
89 #define KEY_SUSPENDED "suspended"
90 #define KEY_PREVIOUS "previous"
91 #define KEY_SUSPENDER "suspender"
92 #define KEY_ISSUED "issued"
93 #define KEY_REVOKED "revoked"
94 #define KEY_SUSPEND_EXPIRES "suspend_expires"
95 #define KEY_SUSPEND_REASON "suspend_reason"
96 #define KEY_GIVEOWNERSHIP "giveownership"
97 #define KEY_STAFF_ISSUER "staff_issuer"
98 #define KEY_OLD_OWNER "old_owner"
99 #define KEY_TARGET "target"
100 #define KEY_TARGET_ACCESS "target_access"
101 #define KEY_VISITED "visited"
102 #define KEY_TOPIC "topic"
103 #define KEY_GREETING "greeting"
104 #define KEY_USER_GREETING "user_greeting"
105 #define KEY_MODES "modes"
106 #define KEY_FLAGS "flags"
107 #define KEY_OPTIONS "options"
108 #define KEY_USERS "users"
109 #define KEY_BANS "bans" /* for lamers */
110 #define KEY_MAX "max"
111 #define KEY_NOTES "notes"
112 #define KEY_TOPIC_MASK "topic_mask"
113 #define KEY_OWNER_TRANSFER "owner_transfer"
116 #define KEY_LEVEL "level"
117 #define KEY_INFO "info"
118 #define KEY_SEEN "seen"
121 #define KEY_OWNER "owner"
122 #define KEY_REASON "reason"
123 #define KEY_SET "set"
124 #define KEY_DURATION "duration"
125 #define KEY_EXPIRES "expires"
126 #define KEY_TRIGGERED "triggered"
128 #define KEY_GOD_TIMEOUT "god_timeout"
130 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
131 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
133 /* Administrative messages */
134 static const struct message_entry msgtab
[] = {
135 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
137 /* Channel registration */
138 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
139 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
140 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
141 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
142 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
143 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
144 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
145 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
147 /* Do-not-register channels */
148 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
150 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
151 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
152 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
153 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
154 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
155 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
156 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
157 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
158 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
160 /* Channel unregistration */
161 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
162 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
163 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
164 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
167 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
168 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
170 /* Channel merging */
171 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
172 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
173 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
174 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
175 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
177 /* Handle unregistration */
178 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
181 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
182 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
183 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
184 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
185 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
186 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
187 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
188 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
189 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
190 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
191 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
192 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
193 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
194 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
196 /* Removing yourself from a channel. */
197 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
198 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
199 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
201 /* User management */
202 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
203 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
204 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
205 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
206 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
207 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
208 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
209 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
210 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
211 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
212 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
213 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
214 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
215 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
216 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
218 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
219 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
220 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
221 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
222 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
223 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
226 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
227 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
228 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
229 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
230 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
231 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
232 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
233 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
234 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
235 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
236 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
237 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
238 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
239 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
240 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
241 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
242 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
244 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
246 /* Channel management */
247 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
248 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
249 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
251 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
252 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
253 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
254 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
255 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
256 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
257 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
259 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
260 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
261 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
262 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
263 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
264 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
265 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
266 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
267 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
280 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
281 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
282 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
283 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
284 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
285 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
286 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
287 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
288 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
289 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
290 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
291 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
292 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
293 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
294 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
296 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
297 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
298 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
299 { "CSMSG_USET_INFO", "$bInfo $b %s" },
301 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
302 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
303 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
304 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
308 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
309 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
310 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
311 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
313 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
314 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
315 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
316 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
317 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
318 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
319 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
321 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
322 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
323 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
324 { "CSMSG_PROTECT_NONE", "No users will be protected." },
325 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
326 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
327 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
329 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
330 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
331 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
332 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
333 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
335 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
336 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
337 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
338 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
339 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
341 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
342 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
343 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
344 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
345 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
347 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
348 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
349 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
350 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
351 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
352 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
354 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
355 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
356 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
357 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
358 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
359 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
360 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
361 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
362 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
364 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
365 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
366 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
368 /* Channel userlist */
369 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
370 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
371 /* uncomment if needed to adujust styles (and change code below)
372 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
373 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
374 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
375 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
376 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
377 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
379 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
380 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
381 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
383 /* Channel note list */
384 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
385 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
386 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
387 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
388 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
389 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
390 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
391 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
392 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
393 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
394 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
395 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
396 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
397 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
398 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
400 /* Channel [un]suspension */
401 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
402 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
403 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
404 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
405 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
406 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
407 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
409 /* Access information */
410 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
411 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
412 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
413 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
414 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
415 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
416 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
417 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
418 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
419 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
420 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
422 /* Seen information */
423 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
424 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
425 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
426 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
428 /* Names information */
429 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
430 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
432 /* Channel information */
433 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
434 { "CSMSG_BAR", "----------------------------------------"},
435 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
436 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
437 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
438 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
439 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
440 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
441 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
442 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
443 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
444 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
445 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
446 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
447 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
452 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
453 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
454 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
455 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
456 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
457 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
458 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
459 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
460 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
462 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
463 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
464 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
465 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
466 { "CSMSG_PEEK_OPS", "$bOps:$b" },
467 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
468 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
470 /* Network information */
471 { "CSMSG_NETWORK_INFO", "Network Information:" },
472 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
473 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
474 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
475 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
476 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
477 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
478 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
479 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
482 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
483 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
484 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
486 /* Channel searches */
487 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
488 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
489 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
490 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
492 /* Channel configuration */
493 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
494 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
495 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
496 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
499 { "CSMSG_USER_OPTIONS", "User Options:" },
500 // { "CSMSG_USER_PROTECTED", "That user is protected." },
503 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
504 { "CSMSG_PING_RESPONSE", "Pong!" },
505 { "CSMSG_WUT_RESPONSE", "wut" },
506 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
507 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
508 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
509 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
510 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
511 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
512 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
515 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
516 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
517 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
518 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
522 /* eject_user and unban_user flags */
523 #define ACTION_KICK 0x0001
524 #define ACTION_BAN 0x0002
525 #define ACTION_ADD_LAMER 0x0004
526 #define ACTION_ADD_TIMED_LAMER 0x0008
527 #define ACTION_UNBAN 0x0010
528 #define ACTION_DEL_LAMER 0x0020
530 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
531 #define MODELEN 40 + KEYLEN
535 #define CSFUNC_ARGS user, channel, argc, argv, cmd
537 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
538 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
539 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
540 reply("MSG_MISSING_PARAMS", argv[0]); \
544 DECLARE_LIST(dnrList
, struct do_not_register
*);
545 DEFINE_LIST(dnrList
, struct do_not_register
*);
547 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
549 struct userNode
*chanserv
;
552 extern struct string_list
*autojoin_channels
;
553 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
554 static struct log_type
*CS_LOG
;
555 struct adduserPending
* adduser_pendings
= NULL
;
556 unsigned int adduser_pendings_count
= 0;
557 unsigned long god_timeout
;
561 struct channelList support_channels
;
562 struct mod_chanmode default_modes
;
564 unsigned long db_backup_frequency
;
565 unsigned long channel_expire_frequency
;
566 unsigned long ban_timeout_frequency
;
569 unsigned int adjust_delay
;
570 long channel_expire_delay
;
571 unsigned int nodelete_level
;
573 unsigned int adjust_threshold
;
574 int join_flood_threshold
;
576 unsigned int greeting_length
;
577 unsigned int refresh_period
;
578 unsigned int giveownership_period
;
580 unsigned int max_owned
;
581 unsigned int max_chan_users
;
582 unsigned int max_chan_bans
; /* lamers */
583 unsigned int max_userinfo_length
;
585 struct string_list
*set_shows
;
586 struct string_list
*eightball
;
587 struct string_list
*old_ban_names
;
589 const char *ctcp_short_ban_duration
;
590 const char *ctcp_long_ban_duration
;
592 const char *irc_operator_epithet
;
593 const char *network_helper_epithet
;
594 const char *support_helper_epithet
;
599 struct userNode
*user
;
600 struct userNode
*bot
;
601 struct chanNode
*channel
;
603 unsigned short lowest
;
604 unsigned short highest
;
605 struct userData
**users
;
606 struct helpfile_table table
;
609 enum note_access_type
611 NOTE_SET_CHANNEL_ACCESS
,
612 NOTE_SET_CHANNEL_SETTER
,
616 enum note_visible_type
619 NOTE_VIS_CHANNEL_USERS
,
625 enum note_access_type set_access_type
;
627 unsigned int min_opserv
;
628 unsigned short min_ulevel
;
630 enum note_visible_type visible_type
;
631 unsigned int max_length
;
638 struct note_type
*type
;
639 char setter
[NICKSERV_HANDLE_LEN
+1];
643 static unsigned int registered_channels
;
644 static unsigned int banCount
;
646 static const struct {
649 unsigned short level
;
651 } accessLevels
[] = { /* MUST be orderd less to most! */
652 { "peon", "Peon", UL_PEON
, '+' },
653 { "halfop", "HalfOp", UL_HALFOP
, '%' },
654 { "op", "Op", UL_OP
, '@' },
655 { "manager", "Manager", UL_MANAGER
, '%' },
656 { "coowner", "Coowner", UL_COOWNER
, '*' },
657 { "owner", "Owner", UL_OWNER
, '!' },
658 { "helper", "BUG:", UL_HELPER
, 'X' }
661 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
662 static const struct {
665 unsigned short default_value
;
666 unsigned int old_idx
;
667 unsigned int old_flag
;
668 unsigned short flag_value
;
670 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
671 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
672 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
673 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
674 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
675 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
676 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
677 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
678 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
681 struct charOptionValues
{
684 } automodeValues
[] = {
685 { 'n', "CSMSG_AUTOMODE_NONE" },
686 { 'y', "CSMSG_AUTOMODE_NORMAL" },
687 { 'v', "CSMSG_AUTOMODE_VOICE" },
688 { 'h', "CSMSG_AUTOMODE_HOP" },
689 { 'o', "CSMSG_AUTOMODE_OP" },
690 { 'm', "CSMSG_AUTOMODE_MUTE" },
691 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
692 }, protectValues
[] = {
693 { 'a', "CSMSG_PROTECT_ALL" },
694 { 'e', "CSMSG_PROTECT_EQUAL" },
695 { 'l', "CSMSG_PROTECT_LOWER" },
696 { 'n', "CSMSG_PROTECT_NONE" }
698 { 'd', "CSMSG_TOYS_DISABLED" },
699 { 'n', "CSMSG_TOYS_PRIVATE" },
700 { 'p', "CSMSG_TOYS_PUBLIC" }
701 }, topicRefreshValues
[] = {
702 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
703 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
704 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
705 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
706 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
707 }, ctcpReactionValues
[] = {
708 { 'n', "CSMSG_CTCPREACTION_NONE" },
709 { 'k', "CSMSG_CTCPREACTION_KICK" },
710 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
711 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
712 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
713 }, banTimeoutValues
[] = {
714 { '0', "CSMSG_BANTIMEOUT_NONE" },
715 { '1', "CSMSG_BANTIMEOUT_10M" },
716 { '2', "CSMSG_BANTIMEOUT_2H" },
717 { '3', "CSMSG_BANTIMEOUT_4H" },
718 { '4', "CSMSG_BANTIMEOUT_1D" },
719 { '5', "CSMSG_BANTIMEOUT_1W" }
722 { 'n', "CSMSG_RESYNC_NEVER" },
723 { '1', "CSMSG_RESYNC_3_HOURS" },
724 { '2', "CSMSG_RESYNC_6_HOURS" },
725 { '3', "CSMSG_RESYNC_12_HOURS" },
726 { '4', "CSMSG_RESYNC_24_HOURS" }
729 static const struct {
733 unsigned int old_idx
;
735 struct charOptionValues
*values
;
737 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
738 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
739 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
740 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
741 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
742 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
743 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
746 struct userData
*helperList
;
747 struct chanData
*channelList
;
748 static struct module *chanserv_module
;
749 static unsigned int userCount
;
750 unsigned int chanserv_read_version
= 0; /* db version control */
752 #define CHANSERV_DB_VERSION 2
754 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
755 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
758 user_level_from_name(const char *name
, unsigned short clamp_level
)
760 unsigned int level
= 0, ii
;
762 level
= strtoul(name
, NULL
, 10);
763 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
764 if(!irccasecmp(name
, accessLevels
[ii
].name
))
765 level
= accessLevels
[ii
].level
;
766 if(level
> clamp_level
)
772 user_level_name_from_level(int level
)
780 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
781 if(level
>= accessLevels
[ii
].level
)
782 highest
= accessLevels
[ii
].title
;
788 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
791 *minl
= strtoul(arg
, &sep
, 10);
799 *maxl
= strtoul(sep
+1, &sep
, 10);
807 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
809 struct userData
*uData
, **head
;
811 if(!channel
|| !handle
)
814 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
815 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
817 for(uData
= helperList
;
818 uData
&& uData
->handle
!= handle
;
819 uData
= uData
->next
);
823 uData
= calloc(1, sizeof(struct userData
));
824 uData
->handle
= handle
;
826 uData
->access
= UL_HELPER
;
832 uData
->next
= helperList
;
834 helperList
->prev
= uData
;
842 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
843 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
846 head
= &(channel
->users
);
849 if(uData
&& (uData
!= *head
))
851 /* Shuffle the user to the head of whatever list he was in. */
853 uData
->next
->prev
= uData
->prev
;
855 uData
->prev
->next
= uData
->next
;
861 (**head
).prev
= uData
;
868 /* Returns non-zero if user has at least the minimum access.
869 * exempt_owner is set when handling !set, so the owner can set things
872 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
874 struct userData
*uData
;
875 struct chanData
*cData
= channel
->channel_info
;
876 unsigned short minimum
= cData
->lvlOpts
[opt
];
879 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
882 if(minimum
<= uData
->access
)
884 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
889 /* Scan for other users authenticated to the same handle
890 still in the channel. If so, keep them listed as present.
892 user is optional, if not null, it skips checking that userNode
893 (for the handle_part function) */
895 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
899 if(IsSuspended(uData
->channel
)
900 || IsUserSuspended(uData
)
901 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
913 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
915 unsigned int eflags
, argc
;
917 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
919 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
920 if(!channel
->channel_info
921 || IsSuspended(channel
->channel_info
)
923 || !ircncasecmp(text
, "ACTION ", 7))
925 /* We dont punish people we know -Rubin
926 * * Figure out the minimum level needed to CTCP the channel *
928 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
931 /* If they are a user of the channel, they are exempt */
932 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
934 /* We need to enforce against them; do so. */
937 argv
[1] = user
->nick
;
939 if(GetUserMode(channel
, user
))
940 eflags
|= ACTION_KICK
;
941 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
942 default: case 'n': return;
944 eflags
|= ACTION_KICK
;
947 eflags
|= ACTION_BAN
;
950 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
951 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
954 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
955 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
958 argv
[argc
++] = bad_ctcp_reason
;
959 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
963 chanserv_create_note_type(const char *name
)
965 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
966 strcpy(ntype
->name
, name
);
968 dict_insert(note_types
, ntype
->name
, ntype
);
973 chanserv_deref_note_type(void *data
)
975 struct note_type
*ntype
= data
;
977 if(--ntype
->refs
> 0)
983 chanserv_flush_note_type(struct note_type
*ntype
)
985 struct chanData
*cData
;
986 for(cData
= channelList
; cData
; cData
= cData
->next
)
987 dict_remove(cData
->notes
, ntype
->name
);
991 chanserv_truncate_notes(struct note_type
*ntype
)
993 struct chanData
*cData
;
995 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
997 for(cData
= channelList
; cData
; cData
= cData
->next
) {
998 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
1001 if(strlen(note
->note
) <= ntype
->max_length
)
1003 dict_remove2(cData
->notes
, ntype
->name
, 1);
1004 note
= realloc(note
, size
);
1005 note
->note
[ntype
->max_length
] = 0;
1006 dict_insert(cData
->notes
, ntype
->name
, note
);
1010 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1012 static struct note
*
1013 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1016 unsigned int len
= strlen(text
);
1018 if(len
> type
->max_length
) len
= type
->max_length
;
1019 note
= calloc(1, sizeof(*note
) + len
);
1021 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1022 memcpy(note
->note
, text
, len
);
1023 note
->note
[len
] = 0;
1024 dict_insert(channel
->notes
, type
->name
, note
);
1030 chanserv_free_note(void *data
)
1032 struct note
*note
= data
;
1034 chanserv_deref_note_type(note
->type
);
1035 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1039 static MODCMD_FUNC(cmd_createnote
) {
1040 struct note_type
*ntype
;
1041 unsigned int arg
= 1, existed
= 0, max_length
;
1043 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1046 ntype
= chanserv_create_note_type(argv
[arg
]);
1047 if(!irccasecmp(argv
[++arg
], "privileged"))
1050 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1051 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1053 else if(!irccasecmp(argv
[arg
], "channel"))
1055 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1058 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1061 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1062 ntype
->set_access
.min_ulevel
= ulvl
;
1064 else if(!irccasecmp(argv
[arg
], "setter"))
1066 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1070 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1074 if(!irccasecmp(argv
[++arg
], "privileged"))
1075 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1076 else if(!irccasecmp(argv
[arg
], "channel_users"))
1077 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1078 else if(!irccasecmp(argv
[arg
], "all"))
1079 ntype
->visible_type
= NOTE_VIS_ALL
;
1081 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1085 if((arg
+1) >= argc
) {
1086 reply("MSG_MISSING_PARAMS", argv
[0]);
1089 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1090 if(max_length
< 20 || max_length
> 450)
1092 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1095 if(existed
&& (max_length
< ntype
->max_length
))
1097 ntype
->max_length
= max_length
;
1098 chanserv_truncate_notes(ntype
);
1100 ntype
->max_length
= max_length
;
1103 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1105 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1110 dict_remove(note_types
, ntype
->name
);
1114 static MODCMD_FUNC(cmd_removenote
) {
1115 struct note_type
*ntype
;
1118 ntype
= dict_find(note_types
, argv
[1], NULL
);
1119 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1122 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1129 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1132 chanserv_flush_note_type(ntype
);
1134 dict_remove(note_types
, argv
[1]);
1135 reply("CSMSG_NOTE_DELETED", argv
[1]);
1140 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1144 if(orig
->modes_set
& change
->modes_clear
)
1146 if(orig
->modes_clear
& change
->modes_set
)
1148 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1149 && strcmp(orig
->new_key
, change
->new_key
))
1151 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1152 && (orig
->new_limit
!= change
->new_limit
))
1157 static char max_length_text
[MAXLEN
+1][16];
1159 static struct helpfile_expansion
1160 chanserv_expand_variable(const char *variable
)
1162 struct helpfile_expansion exp
;
1164 if(!irccasecmp(variable
, "notes"))
1167 exp
.type
= HF_TABLE
;
1168 exp
.value
.table
.length
= 1;
1169 exp
.value
.table
.width
= 3;
1170 exp
.value
.table
.flags
= 0;
1171 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1172 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1173 exp
.value
.table
.contents
[0][0] = "Note Type";
1174 exp
.value
.table
.contents
[0][1] = "Visibility";
1175 exp
.value
.table
.contents
[0][2] = "Max Length";
1176 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1178 struct note_type
*ntype
= iter_data(it
);
1181 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1182 row
= exp
.value
.table
.length
++;
1183 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1184 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1185 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1186 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1188 if(!max_length_text
[ntype
->max_length
][0])
1189 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1190 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1195 exp
.type
= HF_STRING
;
1196 exp
.value
.str
= NULL
;
1200 static struct chanData
*
1201 register_channel(struct chanNode
*cNode
, char *registrar
)
1203 struct chanData
*channel
;
1204 enum levelOption lvlOpt
;
1205 enum charOption chOpt
;
1207 channel
= calloc(1, sizeof(struct chanData
));
1209 channel
->notes
= dict_new();
1210 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1212 channel
->registrar
= strdup(registrar
);
1213 channel
->registered
= now
;
1214 channel
->visited
= now
;
1215 channel
->limitAdjusted
= now
;
1216 channel
->ownerTransfer
= now
;
1217 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1218 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1219 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1220 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1221 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1223 channel
->prev
= NULL
;
1224 channel
->next
= channelList
;
1227 channelList
->prev
= channel
;
1228 channelList
= channel
;
1229 registered_channels
++;
1231 channel
->channel
= cNode
;
1233 cNode
->channel_info
= channel
;
1238 static struct userData
*
1239 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1241 struct userData
*ud
;
1243 if(access
> UL_OWNER
)
1246 ud
= calloc(1, sizeof(*ud
));
1247 ud
->channel
= channel
;
1248 ud
->handle
= handle
;
1250 ud
->access
= access
;
1251 ud
->info
= info
? strdup(info
) : NULL
;
1254 ud
->next
= channel
->users
;
1256 channel
->users
->prev
= ud
;
1257 channel
->users
= ud
;
1259 channel
->userCount
++;
1263 ud
->u_next
= ud
->handle
->channels
;
1265 ud
->u_next
->u_prev
= ud
;
1266 ud
->handle
->channels
= ud
;
1268 ud
->flags
= USER_FLAGS_DEFAULT
;
1272 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1275 del_channel_user(struct userData
*user
, int do_gc
)
1277 struct chanData
*channel
= user
->channel
;
1279 channel
->userCount
--;
1283 user
->prev
->next
= user
->next
;
1285 channel
->users
= user
->next
;
1287 user
->next
->prev
= user
->prev
;
1290 user
->u_prev
->u_next
= user
->u_next
;
1292 user
->handle
->channels
= user
->u_next
;
1294 user
->u_next
->u_prev
= user
->u_prev
;
1298 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1299 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1300 unregister_channel(channel
, "lost all users.");
1304 static struct adduserPending
*
1305 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1307 struct adduserPending
*ap
;
1308 ap
= calloc(1,sizeof(struct adduserPending
));
1309 ap
->channel
= channel
;
1312 ap
->created
= time(NULL
);
1314 /* ap->prev defaults to NULL already.. */
1315 ap
->next
= adduser_pendings
;
1316 if(adduser_pendings
)
1317 adduser_pendings
->prev
= ap
;
1318 adduser_pendings
= ap
;
1319 adduser_pendings_count
++;
1324 del_adduser_pending(struct adduserPending
*ap
)
1327 ap
->prev
->next
= ap
->next
;
1329 adduser_pendings
= ap
->next
;
1332 ap
->next
->prev
= ap
->prev
;
1336 static void expire_adduser_pending();
1338 /* find_adduser_pending(channel, user) will find an arbitrary record
1339 * from user, channel, or user and channel.
1340 * if user or channel are NULL, they will match any records.
1342 static struct adduserPending
*
1343 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1345 struct adduserPending
*ap
;
1347 expire_adduser_pending(); /* why not here.. */
1349 if(!channel
&& !user
) /* 2 nulls matches all */
1350 return(adduser_pendings
);
1351 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1353 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1360 /* Remove all pendings for a user or channel
1362 * called in nickserv.c DelUser() and proto-* unregister_channel()
1365 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1367 struct adduserPending
*ap
;
1369 /* So this is a bit wastefull, i hate dealing with linked lists.
1370 * if its a problem we'll rewrite it right */
1371 while((ap
= find_adduser_pending(channel
, user
))) {
1372 del_adduser_pending(ap
);
1376 /* Called from nickserv.c cmd_auth after someone auths */
1378 process_adduser_pending(struct userNode
*user
)
1380 struct adduserPending
*ap
;
1381 if(!user
->handle_info
)
1382 return; /* not associated with an account */
1383 while((ap
= find_adduser_pending(NULL
, user
)))
1385 struct userData
*actee
;
1386 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1388 /* Already on the userlist. do nothing*/
1392 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1393 scan_user_presence(actee
, NULL
);
1395 del_adduser_pending(ap
);
1400 expire_adduser_pending()
1402 struct adduserPending
*ap
, *ap_next
;
1403 ap
= adduser_pendings
;
1406 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1408 ap_next
= ap
->next
; /* save next */
1409 del_adduser_pending(ap
); /* free and relink */
1410 ap
= ap_next
; /* advance */
1417 static void expire_ban(void *data
);
1420 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1423 unsigned int ii
, l1
, l2
;
1428 bd
= malloc(sizeof(struct banData
));
1430 bd
->channel
= channel
;
1432 bd
->triggered
= triggered
;
1433 bd
->expires
= expires
;
1435 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1437 extern const char *hidden_host_suffix
;
1438 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1442 l2
= strlen(old_name
);
1445 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1447 new_mask
= alloca(MAXLEN
);
1448 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1451 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1453 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1454 bd
->reason
= strdup(reason
);
1457 timeq_add(expires
, expire_ban
, bd
);
1460 bd
->next
= channel
->bans
; /* lamers */
1462 channel
->bans
->prev
= bd
;
1464 channel
->banCount
++;
1471 del_channel_ban(struct banData
*ban
)
1473 ban
->channel
->banCount
--;
1477 ban
->prev
->next
= ban
->next
;
1479 ban
->channel
->bans
= ban
->next
;
1482 ban
->next
->prev
= ban
->prev
;
1485 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1494 expire_ban(void *data
) /* lamer.. */
1496 struct banData
*bd
= data
;
1497 if(!IsSuspended(bd
->channel
))
1499 struct banList bans
;
1500 struct mod_chanmode change
;
1502 bans
= bd
->channel
->channel
->banlist
;
1503 mod_chanmode_init(&change
);
1504 for(ii
=0; ii
<bans
.used
; ii
++)
1506 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1509 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1510 change
.args
[0].u
.hostmask
= bd
->mask
;
1511 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1517 del_channel_ban(bd
);
1520 static void chanserv_expire_suspension(void *data
);
1523 unregister_channel(struct chanData
*channel
, const char *reason
)
1525 struct mod_chanmode change
;
1526 char msgbuf
[MAXLEN
];
1528 /* After channel unregistration, the following must be cleaned
1530 - Channel information.
1532 - Channel bans. (lamers)
1533 - Channel suspension data.
1534 - adduser_pending data.
1535 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1541 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1545 mod_chanmode_init(&change
);
1546 change
.modes_clear
|= MODE_REGISTERED
;
1547 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1550 wipe_adduser_pending(channel
->channel
, NULL
);
1552 while(channel
->users
)
1553 del_channel_user(channel
->users
, 0);
1555 while(channel
->bans
)
1556 del_channel_ban(channel
->bans
);
1558 free(channel
->topic
);
1559 free(channel
->registrar
);
1560 free(channel
->greeting
);
1561 free(channel
->user_greeting
);
1562 free(channel
->topic_mask
);
1565 channel
->prev
->next
= channel
->next
;
1567 channelList
= channel
->next
;
1570 channel
->next
->prev
= channel
->prev
;
1572 if(channel
->suspended
)
1574 struct chanNode
*cNode
= channel
->channel
;
1575 struct suspended
*suspended
, *next_suspended
;
1577 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1579 next_suspended
= suspended
->previous
;
1580 free(suspended
->suspender
);
1581 free(suspended
->reason
);
1582 if(suspended
->expires
)
1583 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1588 cNode
->channel_info
= NULL
;
1590 channel
->channel
->channel_info
= NULL
;
1592 dict_delete(channel
->notes
);
1593 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1594 if(!IsSuspended(channel
))
1595 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1596 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1597 UnlockChannel(channel
->channel
);
1599 registered_channels
--;
1603 expire_channels(UNUSED_ARG(void *data
))
1605 struct chanData
*channel
, *next
;
1606 struct userData
*user
;
1607 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1609 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1610 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1612 for(channel
= channelList
; channel
; channel
= next
)
1614 next
= channel
->next
;
1616 /* See if the channel can be expired. */
1617 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1618 || IsProtected(channel
))
1621 /* Make sure there are no high-ranking users still in the channel. */
1622 for(user
=channel
->users
; user
; user
=user
->next
)
1623 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1628 /* Unregister the channel */
1629 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1630 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1631 unregister_channel(channel
, "registration expired.");
1634 if(chanserv_conf
.channel_expire_frequency
)
1635 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1639 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1641 char protect
= channel
->chOpts
[chProtect
];
1642 struct userData
*cs_victim
, *cs_aggressor
;
1644 /* Don't protect if no one is to be protected, someone is attacking
1645 himself, or if the aggressor is an IRC Operator. */
1646 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1649 /* Don't protect if the victim isn't authenticated (because they
1650 can't be a channel user), unless we are to protect non-users
1652 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1653 if(protect
!= 'a' && !cs_victim
)
1656 /* Protect if the aggressor isn't a user because at this point,
1657 the aggressor can only be less than or equal to the victim. */
1658 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1662 /* If the aggressor was a user, then the victim can't be helped. */
1669 if(cs_victim
->access
> cs_aggressor
->access
)
1674 if(cs_victim
->access
>= cs_aggressor
->access
)
1683 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1685 struct chanData
*cData
= channel
->channel_info
;
1686 struct userData
*cs_victim
;
1688 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1689 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1690 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1693 reply("CSMSG_OPBY_LOCKED");
1695 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1703 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1705 struct chanData
*cData
= channel
->channel_info
;
1706 struct userData
*cs_victim
;
1708 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1709 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1710 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1712 reply("CSMSG_HOPBY_LOCKED");
1721 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1723 if(IsService(victim
))
1725 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1729 if(protect_user(victim
, user
, channel
->channel_info
))
1731 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1739 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1741 if(IsService(victim
))
1743 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1747 if(protect_user(victim
, user
, channel
->channel_info
))
1749 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1756 static struct do_not_register
*
1757 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1759 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1760 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1761 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1762 strcpy(dnr
->reason
, reason
);
1764 if(dnr
->chan_name
[0] == '*')
1765 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1766 else if(strpbrk(dnr
->chan_name
, "*?"))
1767 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1769 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1773 static struct dnrList
1774 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1776 struct dnrList list
;
1778 struct do_not_register
*dnr
;
1780 dnrList_init(&list
);
1781 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1782 dnrList_append(&list
, dnr
);
1783 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1784 dnrList_append(&list
, dnr
);
1786 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1787 if(match_ircglob(chan_name
, iter_key(it
)))
1788 dnrList_append(&list
, iter_data(it
));
1793 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1795 struct dnrList list
;
1796 struct do_not_register
*dnr
;
1798 char buf
[INTERVALLEN
];
1800 list
= chanserv_find_dnrs(chan_name
, handle
);
1801 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1803 dnr
= list
.list
[ii
];
1806 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1807 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1810 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1813 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1818 struct do_not_register
*
1819 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1821 struct do_not_register
*dnr
;
1824 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1828 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1830 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1831 if(match_ircglob(chan_name
, iter_key(it
)))
1832 return iter_data(it
);
1837 static CHANSERV_FUNC(cmd_noregister
)
1840 struct do_not_register
*dnr
;
1841 char buf
[INTERVALLEN
];
1842 unsigned int matches
;
1848 reply("CSMSG_DNR_SEARCH_RESULTS");
1849 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1852 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1854 dnr
= iter_data(it
);
1856 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1858 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1861 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1863 dnr
= iter_data(it
);
1865 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1867 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1870 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1872 dnr
= iter_data(it
);
1874 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1876 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1881 reply("MSG_MATCH_COUNT", matches
);
1883 reply("MSG_NO_MATCHES");
1889 if(!IsChannelName(target
) && (*target
!= '*'))
1891 reply("CSMSG_NOT_DNR", target
);
1897 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1898 if((*target
== '*') && !get_handle_info(target
+ 1))
1900 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1903 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1904 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1908 reply("CSMSG_DNR_SEARCH_RESULTS");
1909 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1912 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1914 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1916 reply("MSG_NO_MATCHES");
1920 static CHANSERV_FUNC(cmd_allowregister
)
1922 const char *chan_name
= argv
[1];
1924 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1926 dict_remove(handle_dnrs
, chan_name
+1);
1927 reply("CSMSG_DNR_REMOVED", chan_name
);
1929 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1931 dict_remove(plain_dnrs
, chan_name
);
1932 reply("CSMSG_DNR_REMOVED", chan_name
);
1934 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1936 dict_remove(mask_dnrs
, chan_name
);
1937 reply("CSMSG_DNR_REMOVED", chan_name
);
1941 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1948 chanserv_get_owned_count(struct handle_info
*hi
)
1950 struct userData
*cList
;
1953 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1954 if(cList
->access
== UL_OWNER
)
1959 static CHANSERV_FUNC(cmd_register
)
1961 struct handle_info
*handle
;
1962 struct chanData
*cData
;
1963 struct modeNode
*mn
;
1964 char reason
[MAXLEN
];
1966 unsigned int new_channel
, force
=0;
1967 struct do_not_register
*dnr
;
1970 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
1971 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
1977 if(channel
->channel_info
)
1979 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1983 if(channel
->bad_channel
)
1985 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1989 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1991 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1996 chan_name
= channel
->name
;
2002 reply("MSG_MISSING_PARAMS", cmd
->name
);
2003 svccmd_send_help_brief(user
, chanserv
, cmd
);
2006 if(!IsChannelName(argv
[1]))
2008 reply("MSG_NOT_CHANNEL_NAME");
2012 if(opserv_bad_channel(argv
[1]))
2014 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2019 chan_name
= argv
[1];
2022 if(argc
>= (new_channel
+2))
2024 if(!IsHelping(user
))
2026 reply("CSMSG_PROXY_FORBIDDEN");
2030 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2032 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2033 dnr
= chanserv_is_dnr(chan_name
, handle
);
2035 /* Check if they are over the limit.. */
2036 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2038 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2045 handle
= user
->handle_info
;
2046 dnr
= chanserv_is_dnr(chan_name
, handle
);
2047 /* Check if they are over the limit.. */
2048 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2050 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2053 /* Check if another service is in the channel */
2055 for(n
= 0; n
< channel
->members
.used
; n
++)
2057 mn
= channel
->members
.list
[n
];
2058 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2060 reply("CSMSG_ANOTHER_SERVICE");
2067 if(!IsHelping(user
))
2068 reply("CSMSG_DNR_CHANNEL", chan_name
);
2070 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2074 /* now handled above for message specilization *
2075 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2077 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2083 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2085 cData
= register_channel(channel
, user
->handle_info
->handle
);
2086 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2087 cData
->modes
= chanserv_conf
.default_modes
;
2089 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2090 if (IsOffChannel(cData
))
2092 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2096 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2097 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2098 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2100 mod_chanmode_announce(chanserv
, channel
, change
);
2101 mod_chanmode_free(change
);
2104 /* Initialize the channel's max user record. */
2105 cData
->max
= channel
->members
.used
;
2107 if(handle
!= user
->handle_info
)
2108 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2111 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2112 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_REGISTERED_TO", channel
->name
,
2113 handle
->handle
, user
->handle_info
->handle
);
2118 make_confirmation_string(struct userData
*uData
)
2120 static char strbuf
[16];
2125 for(src
= uData
->handle
->handle
; *src
; )
2126 accum
= accum
* 31 + toupper(*src
++);
2128 for(src
= uData
->channel
->channel
->name
; *src
; )
2129 accum
= accum
* 31 + toupper(*src
++);
2130 sprintf(strbuf
, "%08x", accum
);
2134 static CHANSERV_FUNC(cmd_unregister
)
2137 char reason
[MAXLEN
];
2138 struct chanData
*cData
;
2139 struct userData
*uData
;
2141 cData
= channel
->channel_info
;
2144 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2148 uData
= GetChannelUser(cData
, user
->handle_info
);
2149 if(!uData
|| (uData
->access
< UL_OWNER
))
2151 reply("CSMSG_NO_ACCESS");
2155 if(IsProtected(cData
))
2157 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2161 if(!IsHelping(user
))
2163 const char *confirm_string
;
2164 if(IsSuspended(cData
))
2166 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2169 confirm_string
= make_confirmation_string(uData
);
2170 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2172 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2177 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2178 name
= strdup(channel
->name
);
2179 unregister_channel(cData
, reason
);
2180 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2181 reply("CSMSG_UNREG_SUCCESS", name
);
2187 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2189 extern struct userNode
*spamserv
;
2190 struct mod_chanmode
*change
;
2192 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2194 change
= mod_chanmode_alloc(2);
2196 change
->args
[0].mode
= MODE_CHANOP
;
2197 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2198 change
->args
[1].mode
= MODE_CHANOP
;
2199 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2203 change
= mod_chanmode_alloc(1);
2205 change
->args
[0].mode
= MODE_CHANOP
;
2206 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2209 mod_chanmode_announce(chanserv
, channel
, change
);
2210 mod_chanmode_free(change
);
2213 static CHANSERV_FUNC(cmd_move
)
2215 struct mod_chanmode change
;
2216 struct chanNode
*target
;
2217 struct modeNode
*mn
;
2218 struct userData
*uData
;
2219 struct do_not_register
*dnr
;
2220 int chanserv_join
= 0, spamserv_join
;
2224 if(IsProtected(channel
->channel_info
))
2226 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2230 if(!IsChannelName(argv
[1]))
2232 reply("MSG_NOT_CHANNEL_NAME");
2236 if(opserv_bad_channel(argv
[1]))
2238 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2242 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2244 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2246 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2248 if(!IsHelping(user
))
2249 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2251 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2257 mod_chanmode_init(&change
);
2258 if(!(target
= GetChannel(argv
[1])))
2260 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2261 if(!IsSuspended(channel
->channel_info
))
2264 else if(target
->channel_info
)
2266 reply("CSMSG_ALREADY_REGGED", target
->name
);
2269 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2270 && !IsHelping(user
))
2272 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2275 else if(!IsSuspended(channel
->channel_info
))
2280 /* Clear MODE_REGISTERED from old channel, add it to new. */
2282 change
.modes_clear
= MODE_REGISTERED
;
2283 mod_chanmode_announce(chanserv
, channel
, &change
);
2284 change
.modes_clear
= 0;
2285 change
.modes_set
= MODE_REGISTERED
;
2286 mod_chanmode_announce(chanserv
, target
, &change
);
2289 /* Move the channel_info to the target channel; it
2290 shouldn't be necessary to clear timeq callbacks
2291 for the old channel. */
2292 target
->channel_info
= channel
->channel_info
;
2293 target
->channel_info
->channel
= target
;
2294 channel
->channel_info
= NULL
;
2296 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2299 ss_cs_join_channel(target
, spamserv_join
);
2301 if(!IsSuspended(target
->channel_info
))
2303 char reason2
[MAXLEN
];
2304 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2305 DelChannelUser(chanserv
, channel
, reason2
, 0);
2308 UnlockChannel(channel
);
2309 LockChannel(target
);
2310 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_CHANNEL_MOVED",
2311 channel
->name
, target
->name
, user
->handle_info
->handle
);
2313 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2318 merge_users(struct chanData
*source
, struct chanData
*target
)
2320 struct userData
*suData
, *tuData
, *next
;
2326 /* Insert the source's users into the scratch area. */
2327 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2328 dict_insert(merge
, suData
->handle
->handle
, suData
);
2330 /* Iterate through the target's users, looking for
2331 users common to both channels. The lower access is
2332 removed from either the scratch area or target user
2334 for(tuData
= target
->users
; tuData
; tuData
= next
)
2336 struct userData
*choice
;
2338 next
= tuData
->next
;
2340 /* If a source user exists with the same handle as a target
2341 channel's user, resolve the conflict by removing one. */
2342 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2346 /* Pick the data we want to keep. */
2347 /* If the access is the same, use the later seen time. */
2348 if(suData
->access
== tuData
->access
)
2349 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2350 else /* Otherwise, keep the higher access level. */
2351 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2353 /* Remove the user that wasn't picked. */
2354 if(choice
== tuData
)
2356 dict_remove(merge
, suData
->handle
->handle
);
2357 del_channel_user(suData
, 0);
2360 del_channel_user(tuData
, 0);
2363 /* Move the remaining users to the target channel. */
2364 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2366 suData
= iter_data(it
);
2368 /* Insert the user into the target channel's linked list. */
2369 suData
->prev
= NULL
;
2370 suData
->next
= target
->users
;
2371 suData
->channel
= target
;
2374 target
->users
->prev
= suData
;
2375 target
->users
= suData
;
2377 /* Update the user counts for the target channel; the
2378 source counts are left alone. */
2379 target
->userCount
++;
2382 /* Possible to assert (source->users == NULL) here. */
2383 source
->users
= NULL
;
2388 merge_bans(struct chanData
*source
, struct chanData
*target
)
2390 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2392 /* Hold on to the original head of the target ban list
2393 to avoid comparing source bans with source bans. */
2394 tFront
= target
->bans
;
2396 /* Perform a totally expensive O(n*m) merge, ick. */
2397 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2399 /* Flag to track whether the ban's been moved
2400 to the destination yet. */
2403 /* Possible to assert (sbData->prev == NULL) here. */
2404 sNext
= sbData
->next
;
2406 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2408 tNext
= tbData
->next
;
2410 /* Perform two comparisons between each source
2411 and target ban, conflicts are resolved by
2412 keeping the broader ban and copying the later
2413 expiration and triggered time. */
2414 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2416 /* There is a broader ban in the target channel that
2417 overrides one in the source channel; remove the
2418 source ban and break. */
2419 if(sbData
->expires
> tbData
->expires
)
2420 tbData
->expires
= sbData
->expires
;
2421 if(sbData
->triggered
> tbData
->triggered
)
2422 tbData
->triggered
= sbData
->triggered
;
2423 del_channel_ban(sbData
);
2426 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2428 /* There is a broader ban in the source channel that
2429 overrides one in the target channel; remove the
2430 target ban, fall through and move the source over. */
2431 if(tbData
->expires
> sbData
->expires
)
2432 sbData
->expires
= tbData
->expires
;
2433 if(tbData
->triggered
> sbData
->triggered
)
2434 sbData
->triggered
= tbData
->triggered
;
2435 if(tbData
== tFront
)
2437 del_channel_ban(tbData
);
2440 /* Source bans can override multiple target bans, so
2441 we allow a source to run through this loop multiple
2442 times, but we can only move it once. */
2447 /* Remove the source ban from the source ban list. */
2449 sbData
->next
->prev
= sbData
->prev
;
2451 /* Modify the source ban's associated channel. */
2452 sbData
->channel
= target
;
2454 /* Insert the ban into the target channel's linked list. */
2455 sbData
->prev
= NULL
;
2456 sbData
->next
= target
->bans
;
2459 target
->bans
->prev
= sbData
;
2460 target
->bans
= sbData
;
2462 /* Update the user counts for the target channel. */
2467 /* Possible to assert (source->bans == NULL) here. */
2468 source
->bans
= NULL
;
2472 merge_data(struct chanData
*source
, struct chanData
*target
)
2474 /* Use more recent visited and owner-transfer time; use older
2475 * registered time. Bitwise or may_opchan. Use higher max.
2476 * Do not touch last_refresh, ban count or user counts.
2478 if(source
->visited
> target
->visited
)
2479 target
->visited
= source
->visited
;
2480 if(source
->registered
< target
->registered
)
2481 target
->registered
= source
->registered
;
2482 if(source
->ownerTransfer
> target
->ownerTransfer
)
2483 target
->ownerTransfer
= source
->ownerTransfer
;
2484 if(source
->may_opchan
)
2485 target
->may_opchan
= 1;
2486 if(source
->max
> target
->max
)
2487 target
->max
= source
->max
;
2491 merge_channel(struct chanData
*source
, struct chanData
*target
)
2493 merge_users(source
, target
);
2494 merge_bans(source
, target
);
2495 merge_data(source
, target
);
2498 static CHANSERV_FUNC(cmd_merge
)
2500 struct userData
*target_user
;
2501 struct chanNode
*target
;
2502 char reason
[MAXLEN
];
2506 /* Make sure the target channel exists and is registered to the user
2507 performing the command. */
2508 if(!(target
= GetChannel(argv
[1])))
2510 reply("MSG_INVALID_CHANNEL");
2514 if(!target
->channel_info
)
2516 reply("CSMSG_NOT_REGISTERED", target
->name
);
2520 if(IsProtected(channel
->channel_info
))
2522 reply("CSMSG_MERGE_NODELETE");
2526 if(IsSuspended(target
->channel_info
))
2528 reply("CSMSG_MERGE_SUSPENDED");
2532 if(channel
== target
)
2534 reply("CSMSG_MERGE_SELF");
2538 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2539 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2541 reply("CSMSG_MERGE_NOT_OWNER");
2545 /* Merge the channel structures and associated data. */
2546 merge_channel(channel
->channel_info
, target
->channel_info
);
2547 spamserv_cs_move_merge(user
, channel
, target
, 0);
2548 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2549 unregister_channel(channel
->channel_info
, reason
);
2550 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2554 static CHANSERV_FUNC(cmd_opchan
)
2556 struct mod_chanmode change
;
2557 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2559 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2562 channel
->channel_info
->may_opchan
= 0;
2563 mod_chanmode_init(&change
);
2565 change
.args
[0].mode
= MODE_CHANOP
;
2566 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2567 mod_chanmode_announce(chanserv
, channel
, &change
);
2568 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2572 static CHANSERV_FUNC(cmd_adduser
)
2574 struct userData
*actee
;
2575 struct userData
*actor
;
2576 struct handle_info
*handle
;
2577 unsigned short access
;
2581 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2583 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2587 access
= user_level_from_name(argv
[2], UL_OWNER
);
2590 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2594 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2595 if(actor
->access
<= access
)
2597 reply("CSMSG_NO_BUMP_ACCESS");
2601 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2603 // 'kevin must first authenticate with AuthServ.' is sent to user
2604 struct userNode
*unode
;
2605 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2608 if(find_adduser_pending(channel
, unode
)) {
2609 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2612 if(IsInChannel(channel
, unode
)) {
2613 reply("CSMSG_ADDUSER_PENDING");
2614 add_adduser_pending(channel
, unode
, access
);
2615 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2617 /* this results in user must auth AND not in chan errors. too confusing..
2619 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2627 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2629 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2633 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2634 scan_user_presence(actee
, NULL
);
2635 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2639 static CHANSERV_FUNC(cmd_clvl
)
2641 struct handle_info
*handle
;
2642 struct userData
*victim
;
2643 struct userData
*actor
;
2644 unsigned short new_access
;
2645 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2649 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2651 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2654 if(handle
== user
->handle_info
&& !privileged
)
2656 reply("CSMSG_NO_SELF_CLVL");
2660 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2662 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2666 if(actor
->access
<= victim
->access
&& !privileged
)
2668 reply("MSG_USER_OUTRANKED", handle
->handle
);
2672 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2676 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2680 if(new_access
>= actor
->access
&& !privileged
)
2682 reply("CSMSG_NO_BUMP_ACCESS");
2686 victim
->access
= new_access
;
2687 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2691 static CHANSERV_FUNC(cmd_deluser
)
2693 struct handle_info
*handle
;
2694 struct userData
*victim
;
2695 struct userData
*actor
;
2696 unsigned short access
;
2701 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2703 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2706 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2708 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2714 access
= user_level_from_name(argv
[1], UL_OWNER
);
2717 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2720 if(access
!= victim
->access
)
2722 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2728 access
= victim
->access
;
2731 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2733 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2737 chan_name
= strdup(channel
->name
);
2738 del_channel_user(victim
, 1);
2739 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2745 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2747 struct userData
*actor
, *uData
, *next
;
2749 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2751 if(min_access
> max_access
)
2753 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2757 if((actor
->access
<= max_access
) && !IsHelping(user
))
2759 reply("CSMSG_NO_ACCESS");
2763 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2767 if((uData
->access
>= min_access
)
2768 && (uData
->access
<= max_access
)
2769 && match_ircglob(uData
->handle
->handle
, mask
))
2770 del_channel_user(uData
, 1);
2773 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2777 static CHANSERV_FUNC(cmd_mdelowner
)
2779 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2782 static CHANSERV_FUNC(cmd_mdelcoowner
)
2784 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2787 static CHANSERV_FUNC(cmd_mdelmanager
)
2789 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2792 static CHANSERV_FUNC(cmd_mdelop
)
2794 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2797 static CHANSERV_FUNC(cmd_mdelpeon
)
2799 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2802 static CHANSERV_FUNC(cmd_mdelhalfop
)
2804 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2810 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2812 struct banData
*bData
, *next
;
2813 char interval
[INTERVALLEN
];
2818 limit
= now
- duration
;
2819 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2823 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2826 del_channel_ban(bData
);
2830 intervalString(interval
, duration
, user
->handle_info
);
2831 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2836 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
)
2838 struct userData
*actor
, *uData
, *next
;
2839 char interval
[INTERVALLEN
];
2843 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2844 if(min_access
> max_access
)
2846 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2850 if((actor
->access
<= max_access
) && !IsHelping(user
))
2852 reply("CSMSG_NO_ACCESS");
2857 limit
= now
- duration
;
2858 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2862 if((uData
->seen
> limit
)
2864 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2867 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2868 || (!max_access
&& (uData
->access
< actor
->access
)))
2870 del_channel_user(uData
, 1);
2878 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2880 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2884 static CHANSERV_FUNC(cmd_trim
)
2886 unsigned long duration
;
2887 unsigned short min_level
, max_level
;
2892 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2893 duration
= ParseInterval(argv
[2]);
2896 reply("CSMSG_CANNOT_TRIM");
2900 if(!irccasecmp(argv
[1], "lamers"))
2902 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
2905 else if(!irccasecmp(argv
[1], "users"))
2907 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
2910 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2912 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
2915 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2917 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
2922 reply("CSMSG_INVALID_TRIM", argv
[1]);
2927 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2928 to the user. cmd_all takes advantage of this. */
2929 static CHANSERV_FUNC(cmd_up
)
2931 struct mod_chanmode change
;
2932 struct userData
*uData
;
2935 mod_chanmode_init(&change
);
2937 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2938 if(!change
.args
[0].u
.member
)
2941 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2945 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2949 reply("CSMSG_GODMODE_UP", argv
[0]);
2952 else if(uData
->access
>= UL_OP
)
2954 change
.args
[0].mode
= MODE_CHANOP
;
2955 errmsg
= "CSMSG_ALREADY_OPPED";
2957 else if(uData
->access
>= UL_HALFOP
)
2959 change
.args
[0].mode
= MODE_HALFOP
;
2960 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2962 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2964 change
.args
[0].mode
= MODE_VOICE
;
2965 errmsg
= "CSMSG_ALREADY_VOICED";
2970 reply("CSMSG_NO_ACCESS");
2973 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2974 if(!change
.args
[0].mode
)
2977 reply(errmsg
, channel
->name
);
2980 modcmd_chanmode_announce(&change
);
2984 static CHANSERV_FUNC(cmd_down
)
2986 struct mod_chanmode change
;
2988 mod_chanmode_init(&change
);
2990 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2991 if(!change
.args
[0].u
.member
)
2994 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2998 if(!change
.args
[0].u
.member
->modes
)
3001 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3005 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3006 modcmd_chanmode_announce(&change
);
3010 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
)
3012 struct userData
*cList
;
3014 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3016 if(IsSuspended(cList
->channel
)
3017 || IsUserSuspended(cList
)
3018 || !GetUserMode(cList
->channel
->channel
, user
))
3021 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3027 static CHANSERV_FUNC(cmd_upall
)
3029 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3032 static CHANSERV_FUNC(cmd_downall
)
3034 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3037 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3038 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3041 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
)
3043 unsigned int ii
, valid
;
3044 struct userNode
*victim
;
3045 struct mod_chanmode
*change
;
3047 change
= mod_chanmode_alloc(argc
- 1);
3049 for(ii
=valid
=0; ++ii
< argc
; )
3051 if(!(victim
= GetUserH(argv
[ii
])))
3053 change
->args
[valid
].mode
= mode
;
3054 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3055 if(!change
->args
[valid
].u
.member
)
3057 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3062 change
->argc
= valid
;
3063 if(valid
< (argc
-1))
3064 reply("CSMSG_PROCESS_FAILED");
3067 modcmd_chanmode_announce(change
);
3068 reply(action
, channel
->name
);
3070 mod_chanmode_free(change
);
3074 static CHANSERV_FUNC(cmd_op
)
3076 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3079 static CHANSERV_FUNC(cmd_hop
)
3081 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3084 static CHANSERV_FUNC(cmd_deop
)
3086 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3089 static CHANSERV_FUNC(cmd_dehop
)
3091 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3094 static CHANSERV_FUNC(cmd_voice
)
3096 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3099 static CHANSERV_FUNC(cmd_devoice
)
3101 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3105 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3111 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3113 struct modeNode
*mn
= channel
->members
.list
[ii
];
3115 if(IsService(mn
->user
))
3118 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3121 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3125 victims
[(*victimCount
)++] = mn
;
3131 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3133 struct userNode
*victim
;
3134 struct modeNode
**victims
;
3135 unsigned int offset
, n
, victimCount
, duration
= 0;
3136 char *reason
= "Bye.", *ban
, *name
;
3137 char interval
[INTERVALLEN
];
3139 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3140 REQUIRE_PARAMS(offset
);
3143 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3144 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3146 /* Truncate the reason to a length of TOPICLEN, as
3147 the ircd does; however, leave room for an ellipsis
3148 and the kicker's nick. */
3149 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3153 if((victim
= GetUserH(argv
[1])))
3155 victims
= alloca(sizeof(victims
[0]));
3156 victims
[0] = GetUserMode(channel
, victim
);
3157 /* XXX: The comparison with ACTION_KICK is just because all
3158 * other actions can work on users outside the channel, and we
3159 * want to allow those (e.g. unbans) in that case. If we add
3160 * some other ejection action for in-channel users, change
3162 victimCount
= victims
[0] ? 1 : 0;
3164 if(IsService(victim
))
3167 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3171 if((action
== ACTION_KICK
) && !victimCount
)
3174 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3178 if(protect_user(victim
, user
, channel
->channel_info
))
3180 // This translates to send_message(user, cmd->parent->bot, ...)
3181 // if user is x3 (ctcp action) cmd is null and segfault.
3183 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3187 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3188 name
= victim
->nick
;
3192 if(!is_ircmask(argv
[1]))
3195 reply("MSG_NICK_UNKNOWN", argv
[1]);
3199 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3201 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3204 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3207 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3208 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3210 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3211 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3212 some creativity, but its not x3's job to be the ban censor anyway. */
3213 if(is_overmask(argv
[1]))
3216 reply("CSMSG_LAME_MASK", argv
[1]);
3220 if((action
== ACTION_KICK
) && (victimCount
== 0))
3223 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3227 name
= ban
= strdup(argv
[1]);
3230 /* Truncate the ban in place if necessary; we must ensure
3231 that 'ban' is a valid ban mask before sanitizing it. */
3232 sanitize_ircmask(ban
);
3234 if(action
& ACTION_ADD_LAMER
)
3236 struct banData
*bData
, *next
;
3238 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3241 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3246 if(action
& ACTION_ADD_TIMED_LAMER
)
3248 duration
= ParseInterval(argv
[2]);
3253 reply("CSMSG_DURATION_TOO_LOW");
3257 else if(duration
> (86400 * 365 * 2))
3260 reply("CSMSG_DURATION_TOO_HIGH");
3267 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3269 if(match_ircglobs(bData
->mask
, ban
))
3271 int exact
= !irccasecmp(bData
->mask
, ban
);
3273 /* The ban is redundant; there is already a ban
3274 with the same effect in place. */
3278 free(bData
->reason
);
3279 bData
->reason
= strdup(reason
);
3280 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3282 reply("CSMSG_REASON_CHANGE", ban
);
3286 if(exact
&& bData
->expires
)
3290 /* If the ban matches an existing one exactly,
3291 extend the expiration time if the provided
3292 duration is longer. */
3293 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3295 bData
->expires
= now
+ duration
;
3306 /* Delete the expiration timeq entry and
3307 requeue if necessary. */
3308 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3311 timeq_add(bData
->expires
, expire_ban
, bData
);
3315 /* automated kickban, dont reply */
3318 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3320 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3326 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3333 if(match_ircglobs(ban
, bData
->mask
))
3335 /* The ban we are adding makes previously existing
3336 bans redundant; silently remove them. */
3337 del_channel_ban(bData
);
3341 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
);
3343 name
= ban
= strdup(bData
->mask
);
3347 /* WHAT DOES THIS DO?? -Rubin */
3348 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3350 extern const char *hidden_host_suffix
;
3351 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3353 unsigned int l1
, l2
;
3356 l2
= strlen(old_name
);
3359 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3361 new_mask
= malloc(MAXLEN
);
3362 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3364 name
= ban
= new_mask
;
3369 if(action
& ACTION_BAN
)
3371 unsigned int exists
;
3372 struct mod_chanmode
*change
;
3374 if(channel
->banlist
.used
>= MAXBANS
)
3377 reply("CSMSG_BANLIST_FULL", channel
->name
);
3382 exists
= ChannelBanExists(channel
, ban
);
3383 change
= mod_chanmode_alloc(victimCount
+ 1);
3384 for(n
= 0; n
< victimCount
; ++n
)
3386 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3387 change
->args
[n
].u
.member
= victims
[n
];
3391 change
->args
[n
].mode
= MODE_BAN
;
3392 change
->args
[n
++].u
.hostmask
= ban
;
3396 modcmd_chanmode_announce(change
);
3398 mod_chanmode_announce(chanserv
, channel
, change
);
3399 mod_chanmode_free(change
);
3401 if(exists
&& (action
== ACTION_BAN
))
3404 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3410 if(action
& ACTION_KICK
)
3412 char kick_reason
[MAXLEN
];
3413 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3415 for(n
= 0; n
< victimCount
; n
++)
3416 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3421 /* No response, since it was automated. */
3423 else if(action
& ACTION_ADD_LAMER
)
3426 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3428 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3430 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3431 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3432 else if(action
& ACTION_BAN
)
3433 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3434 else if(action
& ACTION_KICK
&& victimCount
)
3435 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3441 static CHANSERV_FUNC(cmd_kickban
)
3443 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3446 static CHANSERV_FUNC(cmd_kick
)
3448 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3451 static CHANSERV_FUNC(cmd_ban
)
3453 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3456 static CHANSERV_FUNC(cmd_addlamer
)
3458 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3461 static CHANSERV_FUNC(cmd_addtimedlamer
)
3463 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3466 static struct mod_chanmode
*
3467 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3469 struct mod_chanmode
*change
;
3470 unsigned char *match
;
3471 unsigned int ii
, count
;
3473 match
= alloca(bans
->used
);
3476 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3478 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3479 MATCH_USENICK
| MATCH_VISIBLE
);
3486 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3488 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3495 change
= mod_chanmode_alloc(count
);
3496 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3500 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3501 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3503 assert(count
== change
->argc
);
3507 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3509 unsigned int jj
, ii
, count
;
3511 struct chanData
*channel
;
3513 struct mod_chanmode
*change
;
3515 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3516 /* Walk through every channel */
3517 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3518 switch(channel
->chOpts
[chBanTimeout
])
3520 default: case '0': continue; /* Dont remove bans in this chan */
3521 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3522 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3523 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3524 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3525 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3528 /* First find out how many bans were going to unset */
3529 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3530 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3534 /* At least one ban, so setup a removal */
3535 change
= mod_chanmode_alloc(count
);
3537 /* Walk over every ban in this channel.. */
3538 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3539 bn
= channel
->channel
->banlist
.list
[jj
];
3540 if (bn
->set
< bantimeout
) {
3541 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3543 /* Add this ban to the mode change */
3544 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3545 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3547 /* Pull this ban out of the list */
3548 banList_remove(&(channel
->channel
->banlist
), bn
);
3553 /* Send the modes to IRC */
3554 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3556 /* free memory from strdup above */
3557 for(ii
= 0; ii
< count
; ++ii
)
3558 free((char*)change
->args
[ii
].u
.hostmask
);
3560 mod_chanmode_free(change
);
3563 /* Set this function to run again */
3564 if(chanserv_conf
.ban_timeout_frequency
)
3565 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3570 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3572 struct userNode
*actee
;
3578 /* may want to allow a comma delimited list of users... */
3579 if(!(actee
= GetUserH(argv
[1])))
3581 if(!is_ircmask(argv
[1]))
3583 reply("MSG_NICK_UNKNOWN", argv
[1]);
3587 mask
= strdup(argv
[1]);
3590 /* We don't sanitize the mask here because ircu
3592 if(action
& ACTION_UNBAN
)
3594 struct mod_chanmode
*change
;
3595 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3600 modcmd_chanmode_announce(change
);
3601 for(ii
= 0; ii
< change
->argc
; ++ii
)
3602 free((char*)change
->args
[ii
].u
.hostmask
);
3603 mod_chanmode_free(change
);
3608 if(action
& ACTION_DEL_LAMER
)
3610 struct banData
*ban
, *next
;
3612 ban
= channel
->channel_info
->bans
; /* lamers */
3616 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3619 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3624 del_channel_ban(ban
);
3631 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3633 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3639 static CHANSERV_FUNC(cmd_unban
)
3641 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3644 static CHANSERV_FUNC(cmd_dellamer
)
3646 /* it doesn't necessarily have to remove the channel ban - may want
3647 to make that an option. */
3648 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3651 static CHANSERV_FUNC(cmd_unbanme
)
3653 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3654 long flags
= ACTION_UNBAN
;
3656 /* remove permanent bans if the user has the proper access. */
3657 if(uData
->access
>= UL_MANAGER
)
3658 flags
|= ACTION_DEL_LAMER
;
3660 argv
[1] = user
->nick
;
3661 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3664 static CHANSERV_FUNC(cmd_unbanall
)
3666 struct mod_chanmode
*change
;
3669 if(!channel
->banlist
.used
)
3671 reply("CSMSG_NO_BANS", channel
->name
);
3675 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3676 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3678 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3679 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3681 modcmd_chanmode_announce(change
);
3682 for(ii
= 0; ii
< change
->argc
; ++ii
)
3683 free((char*)change
->args
[ii
].u
.hostmask
);
3684 mod_chanmode_free(change
);
3685 reply("CSMSG_BANS_REMOVED", channel
->name
);
3689 static CHANSERV_FUNC(cmd_open
)
3691 struct mod_chanmode
*change
;
3694 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3696 change
= mod_chanmode_alloc(0);
3697 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3698 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3699 && channel
->channel_info
->modes
.modes_set
)
3700 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3701 modcmd_chanmode_announce(change
);
3702 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3703 for(ii
= 0; ii
< change
->argc
; ++ii
)
3704 free((char*)change
->args
[ii
].u
.hostmask
);
3705 mod_chanmode_free(change
);
3709 static CHANSERV_FUNC(cmd_myaccess
)
3711 static struct string_buffer sbuf
;
3712 struct handle_info
*target_handle
;
3713 struct userData
*uData
;
3716 target_handle
= user
->handle_info
;
3717 else if(!IsHelping(user
))
3719 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3722 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3725 if(!target_handle
->channels
)
3727 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3731 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3732 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3734 struct chanData
*cData
= uData
->channel
;
3736 if(uData
->access
> UL_OWNER
)
3738 if(IsProtected(cData
)
3739 && (target_handle
!= user
->handle_info
)
3740 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3743 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3744 if(uData
->flags
== USER_AUTO_OP
)
3745 string_buffer_append(&sbuf
, ',');
3746 if(IsUserSuspended(uData
))
3747 string_buffer_append(&sbuf
, 's');
3748 if(IsUserAutoOp(uData
))
3750 if(uData
->access
>= UL_OP
)
3751 string_buffer_append(&sbuf
, 'o');
3752 else if(uData
->access
>= UL_HALFOP
)
3753 string_buffer_append(&sbuf
, 'h');
3754 else if(uData
->access
>= UL_PEON
)
3755 string_buffer_append(&sbuf
, 'v');
3757 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3758 string_buffer_append(&sbuf
, 'i');
3760 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3762 string_buffer_append_string(&sbuf
, ")]");
3763 string_buffer_append(&sbuf
, '\0');
3764 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3770 static CHANSERV_FUNC(cmd_access
)
3772 struct userNode
*target
;
3773 struct handle_info
*target_handle
;
3774 struct userData
*uData
;
3776 char prefix
[MAXLEN
];
3781 target_handle
= target
->handle_info
;
3783 else if((target
= GetUserH(argv
[1])))
3785 target_handle
= target
->handle_info
;
3787 else if(argv
[1][0] == '*')
3789 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3791 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3797 reply("MSG_NICK_UNKNOWN", argv
[1]);
3801 assert(target
|| target_handle
);
3803 if(target
== chanserv
)
3805 reply("CSMSG_IS_CHANSERV");
3813 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3818 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3821 reply("MSG_AUTHENTICATE");
3827 const char *epithet
= NULL
, *type
= NULL
;
3830 epithet
= chanserv_conf
.irc_operator_epithet
;
3833 else if(IsNetworkHelper(target
))
3835 epithet
= chanserv_conf
.network_helper_epithet
;
3836 type
= "network helper";
3838 else if(IsSupportHelper(target
))
3840 epithet
= chanserv_conf
.support_helper_epithet
;
3841 type
= "support helper";
3845 if(target_handle
->epithet
)
3846 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3848 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3850 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3854 sprintf(prefix
, "%s", target_handle
->handle
);
3857 if(!channel
->channel_info
)
3859 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3863 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3864 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3865 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3867 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3868 /* To prevent possible information leaks, only show infolines
3869 * if the requestor is in the channel or it's their own
3871 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3873 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3875 /* Likewise, only say it's suspended if the user has active
3876 * access in that channel or it's their own entry. */
3877 if(IsUserSuspended(uData
)
3878 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3879 || (user
->handle_info
== uData
->handle
)))
3881 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3886 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3892 /* This is never used...
3894 zoot_list(struct listData *list)
3896 struct userData *uData;
3897 unsigned int start, curr, highest, lowest;
3898 struct helpfile_table tmp_table;
3899 const char **temp, *msg;
3901 if(list->table.length == 1)
3904 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);
3906 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));
3907 msg = user_find_message(list->user, "MSG_NONE");
3908 send_message_type(4, list->user, list->bot, " %s", msg);
3910 tmp_table.width = list->table.width;
3911 tmp_table.flags = list->table.flags;
3912 list->table.contents[0][0] = " ";
3913 highest = list->highest;
3914 if(list->lowest != 0)
3915 lowest = list->lowest;
3916 else if(highest < 100)
3919 lowest = highest - 100;
3920 for(start = curr = 1; curr < list->table.length; )
3922 uData = list->users[curr-1];
3923 list->table.contents[curr++][0] = " ";
3924 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3927 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);
3929 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));
3930 temp = list->table.contents[--start];
3931 list->table.contents[start] = list->table.contents[0];
3932 tmp_table.contents = list->table.contents + start;
3933 tmp_table.length = curr - start;
3934 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3935 list->table.contents[start] = temp;
3937 highest = lowest - 1;
3938 lowest = (highest < 100) ? 0 : (highest - 99);
3945 normal_list(struct listData
*list
)
3949 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
);
3951 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
));
3952 if(list
->table
.length
== 1)
3954 msg
= user_find_message(list
->user
, "MSG_NONE");
3955 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3958 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3961 /* if these need changed, uncomment and customize
3963 clean_list(struct listData *list)
3967 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);
3969 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));
3970 if(list->table.length == 1)
3972 msg = user_find_message(list->user, "MSG_NONE");
3973 send_message_type(4, list->user, list->bot, " %s", msg);
3976 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3980 advanced_list(struct listData *list)
3984 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);
3986 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));
3987 if(list->table.length == 1)
3989 msg = user_find_message(list->user, "MSG_NONE");
3990 send_message_type(4, list->user, list->bot, " %s", msg);
3993 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3997 classic_list(struct listData *list)
4001 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4003 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4004 if(list->table.length == 1)
4006 msg = user_find_message(list->user, "MSG_NONE");
4007 send_message_type(4, list->user, list->bot, " %s", msg);
4010 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4015 userData_access_comp(const void *arg_a
, const void *arg_b
)
4017 const struct userData
*a
= *(struct userData
**)arg_a
;
4018 const struct userData
*b
= *(struct userData
**)arg_b
;
4020 if(a
->access
!= b
->access
)
4021 res
= b
->access
- a
->access
;
4023 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4028 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4030 void (*send_list
)(struct listData
*);
4031 struct userData
*uData
;
4032 struct listData lData
;
4033 unsigned int matches
;
4039 lData
.bot
= cmd
->parent
->bot
;
4040 lData
.channel
= channel
;
4041 lData
.lowest
= lowest
;
4042 lData
.highest
= highest
;
4043 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4044 send_list
= normal_list
;
4045 /* What does the following line do exactly?? */
4046 /*(void)zoot_list; ** since it doesn't show user levels */
4049 if(user->handle_info)
4051 switch(user->handle_info->userlist_style)
4053 case HI_STYLE_CLEAN:
4054 send_list = clean_list;
4056 case HI_STYLE_ADVANCED:
4057 send_list = advanced_list;
4059 case HI_STYLE_CLASSIC:
4060 send_list = classic_list;
4062 case HI_STYLE_NORMAL:
4064 send_list = normal_list;
4069 send_list
= normal_list
;
4071 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4073 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4075 if((uData
->access
< lowest
)
4076 || (uData
->access
> highest
)
4077 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4079 lData
.users
[matches
++] = uData
;
4081 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4083 lData
.table
.length
= matches
+1;
4084 lData
.table
.flags
= TABLE_NO_FREE
;
4085 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4087 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4088 lData
.table
.width
= 5; /* with level = 5 */
4090 lData
.table
.width
= 4; /* without = 4 */
4091 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4092 lData
.table
.contents
[0] = ary
;
4093 if(user
->handle_info
) {
4094 switch(user
->handle_info
->userlist_style
) {
4095 case HI_STYLE_CLASSIC
:
4098 case HI_STYLE_ADVANCED
:
4099 ary
[i
++] = "Access";
4102 case HI_STYLE_CLEAN
:
4103 ary
[i
++] = "Access";
4105 case HI_STYLE_NORMAL
:
4107 ary
[i
++] = "Access";
4112 ary
[i
++] = "Access";
4114 ary
[i
++] = "Account";
4115 ary
[i
] = "Last Seen";
4117 ary
[i
++] = "Status";
4118 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4120 struct userData
*uData
= lData
.users
[matches
-1];
4121 char seen
[INTERVALLEN
];
4124 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4125 lData
.table
.contents
[matches
] = ary
;
4126 if(user
->handle_info
) {
4127 switch(user
->handle_info
->userlist_style
) {
4128 case HI_STYLE_CLASSIC
:
4129 ary
[i
++] = strtab(uData
->access
);
4131 case HI_STYLE_ADVANCED
:
4132 ary
[i
++] = user_level_name_from_level(uData
->access
);
4133 ary
[i
++] = strtab(uData
->access
);
4135 case HI_STYLE_CLEAN
:
4136 ary
[i
++] = user_level_name_from_level(uData
->access
);
4138 case HI_STYLE_NORMAL
:
4140 ary
[i
++] = user_level_name_from_level(uData
->access
);
4145 ary
[i
++] = user_level_name_from_level(uData
->access
);
4147 ary
[i
++] = uData
->handle
->handle
;
4150 else if(!uData
->seen
)
4153 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4154 ary
[i
] = strdup(ary
[i
]);
4156 if(IsUserSuspended(uData
))
4157 ary
[i
++] = "Suspended";
4158 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4159 ary
[i
++] = "Vacation";
4161 ary
[i
++] = "Normal";
4164 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4166 /* Free strdup above */
4167 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4168 free(lData
.table
.contents
[matches
]);
4170 free(lData
.table
.contents
[0]);
4171 free(lData
.table
.contents
);
4175 /* Remove this now that debugging is over? or improve it for
4176 * users? Would it be better tied into USERS somehow? -Rubin */
4177 static CHANSERV_FUNC(cmd_pending
)
4179 struct adduserPending
*ap
;
4180 reply("CSMSG_ADDUSER_PENDING_HEADER");
4181 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4183 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4184 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4185 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4189 static CHANSERV_FUNC(cmd_users
)
4191 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4194 static CHANSERV_FUNC(cmd_wlist
)
4196 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4199 static CHANSERV_FUNC(cmd_clist
)
4201 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4204 static CHANSERV_FUNC(cmd_mlist
)
4206 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4209 static CHANSERV_FUNC(cmd_olist
)
4211 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4214 static CHANSERV_FUNC(cmd_hlist
)
4216 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4219 static CHANSERV_FUNC(cmd_plist
)
4221 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4224 static CHANSERV_FUNC(cmd_lamers
)
4226 struct helpfile_table tbl
;
4227 unsigned int matches
= 0, timed
= 0, ii
;
4228 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4229 const char *msg_never
, *triggered
, *expires
;
4230 struct banData
*ban
, **bans
; /* lamers */
4237 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4238 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4241 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4243 if(search
&& !match_ircglobs(search
, ban
->mask
))
4245 bans
[matches
++] = ban
;
4250 tbl
.length
= matches
+ 1;
4251 tbl
.width
= 4 + timed
;
4253 tbl
.flags
= TABLE_NO_FREE
;
4254 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4255 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4256 tbl
.contents
[0][0] = "Mask";
4257 tbl
.contents
[0][1] = "Set By";
4258 tbl
.contents
[0][2] = "Triggered";
4261 tbl
.contents
[0][3] = "Expires";
4262 tbl
.contents
[0][4] = "Reason";
4265 tbl
.contents
[0][3] = "Reason";
4268 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4270 free(tbl
.contents
[0]);
4275 msg_never
= user_find_message(user
, "MSG_NEVER");
4276 for(ii
= 0; ii
< matches
; )
4282 else if(ban
->expires
)
4283 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4285 expires
= msg_never
;
4288 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4290 triggered
= msg_never
;
4292 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4293 tbl
.contents
[ii
][0] = ban
->mask
;
4294 tbl
.contents
[ii
][1] = ban
->owner
;
4295 tbl
.contents
[ii
][2] = strdup(triggered
);
4298 tbl
.contents
[ii
][3] = strdup(expires
);
4299 tbl
.contents
[ii
][4] = ban
->reason
;
4302 tbl
.contents
[ii
][3] = ban
->reason
;
4304 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4305 /* reply("MSG_MATCH_COUNT", matches); */
4306 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4308 free((char*)tbl
.contents
[ii
][2]);
4310 free((char*)tbl
.contents
[ii
][3]);
4311 free(tbl
.contents
[ii
]);
4313 free(tbl
.contents
[0]);
4320 * return + if the user does NOT have the right to set the topic, and
4321 * the topic is changed.
4324 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4326 struct chanData
*cData
= channel
->channel_info
;
4327 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4329 else if(cData
->topic
)
4330 return irccasecmp(new_topic
, cData
->topic
);
4337 * Makes a givin topic fit into a givin topic mask and returns
4340 * topic_mask - the mask to conform to
4341 * topic - the topic to make conform
4342 * new_topic - the pre-allocated char* to put the new topic into
4344 * modifies: new_topic
4347 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4349 //char *topic_mask = cData->topic_mask;
4351 int pos
=0, starpos
=-1, dpos
=0, len
;
4353 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4360 strcpy(new_topic
, "");
4363 len
= strlen(topic
);
4364 if((dpos
+ len
) > TOPICLEN
)
4365 len
= TOPICLEN
+ 1 - dpos
;
4366 memcpy(new_topic
+dpos
, topic
, len
);
4370 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4371 default: new_topic
[dpos
++] = tchar
; break;
4374 if((dpos
> TOPICLEN
) || tchar
)
4376 strcpy(new_topic
, "");
4379 new_topic
[dpos
] = 0;
4383 static CHANSERV_FUNC(cmd_topic
)
4385 struct chanData
*cData
;
4389 #ifdef WITH_PROTOCOL_P10
4393 cData
= channel
->channel_info
;
4398 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4399 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4400 reply("CSMSG_TOPIC_SET", cData
->topic
);
4404 reply("CSMSG_NO_TOPIC", channel
->name
);
4408 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4409 /* If they say "!topic *", use an empty topic. */
4410 if((topic
[0] == '*') && (topic
[1] == 0))
4413 if(bad_topic(channel
, user
, topic
))
4415 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4420 /* If there is a topicmask set, and the new topic doesnt match, make it */
4421 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4423 char *topic_mask
= cData
->topic_mask
;
4424 char new_topic
[TOPICLEN
+1];
4426 /* make a new topic fitting mask */
4427 conform_topic(topic_mask
, topic
, new_topic
);
4430 /* Topic couldnt fit into mask, was too long */
4431 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4432 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4435 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4437 else /* No mask set, just set the topic */
4438 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4441 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4443 /* Grab the topic and save it as the default topic. */
4445 cData
->topic
= strdup(channel
->topic
);
4451 static CHANSERV_FUNC(cmd_mode
)
4453 struct userData
*uData
;
4454 struct mod_chanmode
*change
;
4459 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4460 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4464 change
= &channel
->channel_info
->modes
;
4465 if(change
->modes_set
|| change
->modes_clear
) {
4466 modcmd_chanmode_announce(change
);
4467 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4469 reply("CSMSG_NO_MODES", channel
->name
);
4473 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4475 base_oplevel
= MAXOPLEVEL
;
4476 else if (uData
->access
>= UL_OWNER
)
4479 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4480 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4484 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4488 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4489 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4492 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4493 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4497 modcmd_chanmode_announce(change
);
4498 mod_chanmode_free(change
);
4499 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4503 static CHANSERV_FUNC(cmd_invite
)
4505 struct userData
*uData
;
4506 struct userNode
*invite
;
4508 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4512 if(!(invite
= GetUserH(argv
[1])))
4514 reply("MSG_NICK_UNKNOWN", argv
[1]);
4521 if(GetUserMode(channel
, invite
))
4523 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4531 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4532 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4535 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4538 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4540 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4541 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4542 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4548 irc_invite(chanserv
, invite
, channel
);
4550 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4555 static CHANSERV_FUNC(cmd_inviteme
)
4557 if(GetUserMode(channel
, user
))
4559 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4562 if(channel
->channel_info
4563 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4565 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4568 irc_invite(cmd
->parent
->bot
, user
, channel
);
4573 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4576 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4578 /* We display things based on two dimensions:
4579 * - Issue time: present or absent
4580 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4581 * (in order of precedence, so something both expired and revoked
4582 * only counts as revoked)
4584 combo
= (suspended
->issued
? 4 : 0)
4585 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4587 case 0: /* no issue time, indefinite expiration */
4588 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4590 case 1: /* no issue time, expires in future */
4591 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4592 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4594 case 2: /* no issue time, expired */
4595 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4596 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4598 case 3: /* no issue time, revoked */
4599 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4600 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4602 case 4: /* issue time set, indefinite expiration */
4603 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4604 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4606 case 5: /* issue time set, expires in future */
4607 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4608 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4609 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4611 case 6: /* issue time set, expired */
4612 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4613 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4614 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4616 case 7: /* issue time set, revoked */
4617 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4618 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4619 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4622 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4628 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4631 const char *fmt
= "%a %b %d %H:%M %Y";
4632 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4634 if(giveownership
->staff_issuer
)
4636 if(giveownership
->reason
)
4637 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4638 giveownership
->target
, giveownership
->target_access
,
4639 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4641 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4642 giveownership
->target
, giveownership
->target_access
,
4643 giveownership
->staff_issuer
, buf
);
4647 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4652 static CHANSERV_FUNC(cmd_info
)
4654 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4655 struct userData
*uData
, *owner
;
4656 struct chanData
*cData
;
4657 struct do_not_register
*dnr
;
4662 cData
= channel
->channel_info
;
4663 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4664 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4667 uData
= GetChannelUser(cData
, user
->handle_info
);
4668 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4670 mod_chanmode_format(&cData
->modes
, modes
);
4671 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4672 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4675 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4679 note
= iter_data(it
);
4680 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4683 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4684 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4687 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4688 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4689 if(owner
->access
== UL_OWNER
)
4690 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4691 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4692 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4693 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4695 privileged
= IsStaff(user
);
4696 /* if(privileged) */
4697 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4698 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4699 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4701 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4702 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4704 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4706 struct suspended
*suspended
;
4707 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4708 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4709 show_suspension_info(cmd
, user
, suspended
);
4711 else if(IsSuspended(cData
))
4713 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4714 show_suspension_info(cmd
, user
, cData
->suspended
);
4716 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4718 struct giveownership
*giveownership
;
4719 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4720 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4721 show_giveownership_info(cmd
, user
, giveownership
);
4723 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4724 reply("CSMSG_CHANNEL_END");
4726 reply("CSMSG_CHANNEL_END_CLEAN");
4730 static CHANSERV_FUNC(cmd_netinfo
)
4732 extern time_t boot_time
;
4733 extern unsigned long burst_length
;
4734 char interval
[INTERVALLEN
];
4736 reply("CSMSG_NETWORK_INFO");
4737 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4738 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4739 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4740 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4741 reply("CSMSG_NETWORK_LAMERS", banCount
);
4742 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4743 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4744 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4749 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4751 struct helpfile_table table
;
4753 struct userNode
*user
;
4758 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4759 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4760 for(nn
=0; nn
<list
->used
; nn
++)
4762 user
= list
->list
[nn
];
4763 if(user
->modes
& skip_flags
)
4767 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4770 nick
= alloca(strlen(user
->nick
)+3);
4771 sprintf(nick
, "(%s)", user
->nick
);
4775 table
.contents
[table
.length
][0] = nick
;
4778 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4781 static CHANSERV_FUNC(cmd_ircops
)
4783 reply("CSMSG_STAFF_OPERS");
4784 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4788 static CHANSERV_FUNC(cmd_helpers
)
4790 reply("CSMSG_STAFF_HELPERS");
4791 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4795 static CHANSERV_FUNC(cmd_staff
)
4797 reply("CSMSG_NETWORK_STAFF");
4798 cmd_ircops(CSFUNC_ARGS
);
4799 cmd_helpers(CSFUNC_ARGS
);
4803 static CHANSERV_FUNC(cmd_peek
)
4805 struct modeNode
*mn
;
4806 char modes
[MODELEN
];
4808 struct helpfile_table table
;
4810 irc_make_chanmode(channel
, modes
);
4812 reply("CSMSG_PEEK_INFO", channel
->name
);
4813 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4815 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4816 reply("CSMSG_PEEK_MODES", modes
);
4817 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4821 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4822 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4823 for(n
= 0; n
< channel
->members
.used
; n
++)
4825 mn
= channel
->members
.list
[n
];
4826 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4828 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4829 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4834 reply("CSMSG_PEEK_OPS");
4835 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4838 reply("CSMSG_PEEK_NO_OPS");
4839 reply("CSMSG_PEEK_END");
4843 static MODCMD_FUNC(cmd_wipeinfo
)
4845 struct handle_info
*victim
;
4846 struct userData
*ud
, *actor
;
4849 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4850 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4852 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4854 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4857 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4859 reply("MSG_USER_OUTRANKED", victim
->handle
);
4865 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4870 resync_channel(struct chanNode
*channel
)
4872 struct mod_chanmode
*changes
;
4873 struct chanData
*cData
= channel
->channel_info
;
4874 unsigned int ii
, used
;
4876 /* 6 = worst case -ovh+ovh on everyone */
4877 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4878 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4880 struct modeNode
*mn
= channel
->members
.list
[ii
];
4881 struct userData
*uData
;
4883 if(IsService(mn
->user
))
4887 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4889 /* If the channel is in no-mode mode, de-mode EVERYONE */
4890 if(cData
->chOpts
[chAutomode
] == 'n')
4894 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4895 changes
->args
[used
++].u
.member
= mn
;
4898 else /* Give various userlevels their modes.. */
4900 if(uData
&& uData
->access
>= UL_OP
)
4902 if(!(mn
->modes
& MODE_CHANOP
))
4904 changes
->args
[used
].mode
= MODE_CHANOP
;
4905 changes
->args
[used
++].u
.member
= mn
;
4908 else if(uData
&& uData
->access
>= UL_HALFOP
)
4910 if(mn
->modes
& MODE_CHANOP
)
4912 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4913 changes
->args
[used
++].u
.member
= mn
;
4915 if(!(mn
->modes
& MODE_HALFOP
))
4917 changes
->args
[used
].mode
= MODE_HALFOP
;
4918 changes
->args
[used
++].u
.member
= mn
;
4921 else if(uData
&& uData
->access
>= UL_PEON
)
4923 if(mn
->modes
& MODE_CHANOP
)
4925 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4926 changes
->args
[used
++].u
.member
= mn
;
4928 if(mn
->modes
& MODE_HALFOP
)
4930 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4931 changes
->args
[used
++].u
.member
= mn
;
4933 /* Don't voice peons if were in mode m */
4934 if( cData
->chOpts
[chAutomode
] == 'm')
4936 if(mn
->modes
& MODE_VOICE
)
4938 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4939 changes
->args
[used
++].u
.member
= mn
;
4942 /* otherwise, make user they do have voice */
4943 else if(!(mn
->modes
& MODE_VOICE
))
4945 changes
->args
[used
].mode
= MODE_VOICE
;
4946 changes
->args
[used
++].u
.member
= mn
;
4949 else /* They arnt on the userlist.. */
4951 /* If we voice everyone, but they dont.. */
4952 if(cData
->chOpts
[chAutomode
] == 'v')
4954 /* Remove anything except v */
4955 if(mn
->modes
& ~MODE_VOICE
)
4957 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4958 changes
->args
[used
++].u
.member
= mn
;
4961 if(!(mn
->modes
& MODE_VOICE
))
4963 changes
->args
[used
].mode
= MODE_VOICE
;
4964 changes
->args
[used
++].u
.member
= mn
;
4967 /* If we hop everyone, but they dont.. */
4968 else if(cData
->chOpts
[chAutomode
] == 'h')
4970 /* Remove anything except h */
4971 if(mn
->modes
& ~MODE_HALFOP
)
4973 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4974 changes
->args
[used
++].u
.member
= mn
;
4977 if(!(mn
->modes
& MODE_HALFOP
))
4979 changes
->args
[used
].mode
= MODE_HALFOP
;
4980 changes
->args
[used
++].u
.member
= mn
;
4983 /* If we op everyone, but they dont.. */
4984 else if(cData
->chOpts
[chAutomode
] == 'o')
4986 /* Remove anything except h */
4987 if(mn
->modes
& ~MODE_CHANOP
)
4989 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4990 changes
->args
[used
++].u
.member
= mn
;
4993 if(!(mn
->modes
& MODE_CHANOP
))
4995 changes
->args
[used
].mode
= MODE_CHANOP
;
4996 changes
->args
[used
++].u
.member
= mn
;
4999 /* they have no excuse for having modes, de-everything them */
5004 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5005 changes
->args
[used
++].u
.member
= mn
;
5011 changes
->argc
= used
;
5012 mod_chanmode_announce(chanserv
, channel
, changes
);
5013 mod_chanmode_free(changes
);
5016 static CHANSERV_FUNC(cmd_resync
)
5018 resync_channel(channel
);
5019 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5023 static CHANSERV_FUNC(cmd_seen
)
5025 struct userData
*uData
;
5026 struct handle_info
*handle
;
5027 char seen
[INTERVALLEN
];
5031 if(!irccasecmp(argv
[1], chanserv
->nick
))
5033 reply("CSMSG_IS_CHANSERV");
5037 if(!(handle
= get_handle_info(argv
[1])))
5039 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5043 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5045 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5050 reply("CSMSG_USER_PRESENT", handle
->handle
);
5051 else if(uData
->seen
)
5052 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5054 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5056 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5057 reply("CSMSG_USER_VACATION", handle
->handle
);
5062 static MODCMD_FUNC(cmd_names
)
5064 struct userNode
*targ
;
5065 struct userData
*targData
;
5066 unsigned int ii
, pos
;
5069 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5071 targ
= channel
->members
.list
[ii
]->user
;
5072 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5075 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5078 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5082 if(IsUserSuspended(targData
))
5084 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5087 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5088 reply("CSMSG_END_NAMES", channel
->name
);
5093 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5095 switch(ntype
->visible_type
)
5097 case NOTE_VIS_ALL
: return 1;
5098 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5099 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5104 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5106 struct userData
*uData
;
5108 switch(ntype
->set_access_type
)
5110 case NOTE_SET_CHANNEL_ACCESS
:
5111 if(!user
->handle_info
)
5113 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5115 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5116 case NOTE_SET_CHANNEL_SETTER
:
5117 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5118 case NOTE_SET_PRIVILEGED
: default:
5119 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5123 static CHANSERV_FUNC(cmd_note
)
5125 struct chanData
*cData
;
5127 struct note_type
*ntype
;
5129 cData
= channel
->channel_info
;
5132 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5136 /* If no arguments, show all visible notes for the channel. */
5142 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5144 note
= iter_data(it
);
5145 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5148 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5149 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5152 reply("CSMSG_NOTELIST_END", channel
->name
);
5154 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5156 /* If one argument, show the named note. */
5159 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5160 && note_type_visible_to_user(cData
, note
->type
, user
))
5162 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5164 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5165 && note_type_visible_to_user(NULL
, ntype
, user
))
5167 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5172 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5176 /* Assume they're trying to set a note. */
5180 ntype
= dict_find(note_types
, argv
[1], NULL
);
5183 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5186 else if(note_type_settable_by_user(channel
, ntype
, user
))
5188 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5189 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5190 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5191 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5192 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5194 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5196 /* The note is viewable to staff only, so return 0
5197 to keep the invocation from getting logged (or
5198 regular users can see it in !events). */
5204 reply("CSMSG_NO_ACCESS");
5211 static CHANSERV_FUNC(cmd_delnote
)
5216 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5217 || !note_type_settable_by_user(channel
, note
->type
, user
))
5219 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5222 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5223 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5227 static CHANSERV_FUNC(cmd_last
)
5233 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5235 if(numoflines
< 1 || numoflines
> 200)
5237 reply("CSMSG_LAST_INVALID");
5240 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5244 static CHANSERV_FUNC(cmd_events
)
5246 struct logSearch discrim
;
5247 struct logReport report
;
5248 unsigned int matches
, limit
;
5250 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5251 if(limit
< 1 || limit
> 200)
5254 memset(&discrim
, 0, sizeof(discrim
));
5255 discrim
.masks
.bot
= chanserv
;
5256 discrim
.masks
.channel_name
= channel
->name
;
5258 discrim
.masks
.command
= argv
[2];
5259 discrim
.limit
= limit
;
5260 discrim
.max_time
= INT_MAX
;
5261 discrim
.severities
= 1 << LOG_COMMAND
;
5262 report
.reporter
= chanserv
;
5264 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5265 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5267 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5269 reply("MSG_MATCH_COUNT", matches
);
5271 reply("MSG_NO_MATCHES");
5275 static CHANSERV_FUNC(cmd_say
)
5281 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5282 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5284 else if(GetUserH(argv
[1]))
5287 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5288 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5292 reply("MSG_NOT_TARGET_NAME");
5298 static CHANSERV_FUNC(cmd_emote
)
5304 /* CTCP is so annoying. */
5305 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5306 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5308 else if(GetUserH(argv
[1]))
5310 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5311 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5315 reply("MSG_NOT_TARGET_NAME");
5321 struct channelList
*
5322 chanserv_support_channels(void)
5324 return &chanserv_conf
.support_channels
;
5327 static CHANSERV_FUNC(cmd_expire
)
5329 int channel_count
= registered_channels
;
5330 expire_channels(NULL
);
5331 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5336 chanserv_expire_suspension(void *data
)
5338 struct suspended
*suspended
= data
;
5339 struct chanNode
*channel
;
5341 if(!suspended
->expires
|| (now
< suspended
->expires
))
5342 suspended
->revoked
= now
;
5343 channel
= suspended
->cData
->channel
;
5344 suspended
->cData
->channel
= channel
;
5345 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5346 if(!IsOffChannel(suspended
->cData
))
5348 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5349 ss_cs_join_channel(channel
, 1);
5353 static CHANSERV_FUNC(cmd_csuspend
)
5355 struct suspended
*suspended
;
5356 char reason
[MAXLEN
];
5357 time_t expiry
, duration
;
5358 struct userData
*uData
;
5362 if(IsProtected(channel
->channel_info
))
5364 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5368 if(argv
[1][0] == '!')
5370 else if(IsSuspended(channel
->channel_info
))
5372 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5373 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5377 if(!strcmp(argv
[1], "0"))
5379 else if((duration
= ParseInterval(argv
[1])))
5380 expiry
= now
+ duration
;
5383 reply("MSG_INVALID_DURATION", argv
[1]);
5387 unsplit_string(argv
+ 2, argc
- 2, reason
);
5389 suspended
= calloc(1, sizeof(*suspended
));
5390 suspended
->revoked
= 0;
5391 suspended
->issued
= now
;
5392 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5393 suspended
->expires
= expiry
;
5394 suspended
->reason
= strdup(reason
);
5395 suspended
->cData
= channel
->channel_info
;
5396 suspended
->previous
= suspended
->cData
->suspended
;
5397 suspended
->cData
->suspended
= suspended
;
5399 if(suspended
->expires
)
5400 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5402 if(IsSuspended(channel
->channel_info
))
5404 suspended
->previous
->revoked
= now
;
5405 if(suspended
->previous
->expires
)
5406 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5408 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENSION_MODIFIED",
5409 channel
->name
, suspended
->suspender
);
5413 /* Mark all users in channel as absent. */
5414 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5423 /* Mark the channel as suspended, then part. */
5424 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5425 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5426 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5427 reply("CSMSG_SUSPENDED", channel
->name
);
5428 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_SUSPENDED_BY",
5429 channel
->name
, suspended
->suspender
);
5434 static CHANSERV_FUNC(cmd_cunsuspend
)
5436 struct suspended
*suspended
;
5438 if(!IsSuspended(channel
->channel_info
))
5440 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5444 suspended
= channel
->channel_info
->suspended
;
5446 /* Expire the suspension and join ChanServ to the channel. */
5447 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5448 chanserv_expire_suspension(suspended
);
5449 reply("CSMSG_UNSUSPENDED", channel
->name
);
5450 global_message_args(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, "CSMSG_UNSUSPENDED_BY",
5451 channel
->name
, user
->handle_info
->handle
);
5455 typedef struct chanservSearch
5463 unsigned long flags
;
5467 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5470 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5475 search
= malloc(sizeof(struct chanservSearch
));
5476 memset(search
, 0, sizeof(*search
));
5479 for(i
= 0; i
< argc
; i
++)
5481 /* Assume all criteria require arguments. */
5484 reply("MSG_MISSING_PARAMS", argv
[i
]);
5488 if(!irccasecmp(argv
[i
], "name"))
5489 search
->name
= argv
[++i
];
5490 else if(!irccasecmp(argv
[i
], "registrar"))
5491 search
->registrar
= argv
[++i
];
5492 else if(!irccasecmp(argv
[i
], "unvisited"))
5493 search
->unvisited
= ParseInterval(argv
[++i
]);
5494 else if(!irccasecmp(argv
[i
], "registered"))
5495 search
->registered
= ParseInterval(argv
[++i
]);
5496 else if(!irccasecmp(argv
[i
], "flags"))
5499 if(!irccasecmp(argv
[i
], "nodelete"))
5500 search
->flags
|= CHANNEL_NODELETE
;
5501 else if(!irccasecmp(argv
[i
], "suspended"))
5502 search
->flags
|= CHANNEL_SUSPENDED
;
5505 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5509 else if(!irccasecmp(argv
[i
], "limit"))
5510 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5513 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5518 if(search
->name
&& !strcmp(search
->name
, "*"))
5520 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5521 search
->registrar
= 0;
5530 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5532 const char *name
= channel
->channel
->name
;
5533 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5534 (search
->registrar
&& !channel
->registrar
) ||
5535 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5536 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5537 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5538 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5545 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5547 struct chanData
*channel
;
5548 unsigned int matches
= 0;
5550 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5552 if(!chanserv_channel_match(channel
, search
))
5562 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5567 search_print(struct chanData
*channel
, void *data
)
5569 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5572 static CHANSERV_FUNC(cmd_search
)
5575 unsigned int matches
;
5576 channel_search_func action
;
5580 if(!irccasecmp(argv
[1], "count"))
5581 action
= search_count
;
5582 else if(!irccasecmp(argv
[1], "print"))
5583 action
= search_print
;
5586 reply("CSMSG_ACTION_INVALID", argv
[1]);
5590 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5594 if(action
== search_count
)
5595 search
->limit
= INT_MAX
;
5597 if(action
== search_print
)
5599 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5600 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5604 matches
= chanserv_channel_search(search
, action
, user
);
5607 reply("MSG_MATCH_COUNT", matches
);
5609 reply("MSG_NO_MATCHES");
5615 static CHANSERV_FUNC(cmd_unvisited
)
5617 struct chanData
*cData
;
5618 time_t interval
= chanserv_conf
.channel_expire_delay
;
5619 char buffer
[INTERVALLEN
];
5620 unsigned int limit
= 25, matches
= 0;
5624 interval
= ParseInterval(argv
[1]);
5626 limit
= atoi(argv
[2]);
5629 intervalString(buffer
, interval
, user
->handle_info
);
5630 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5632 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5634 if((now
- cData
->visited
) < interval
)
5637 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5638 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5645 static MODCMD_FUNC(chan_opt_defaulttopic
)
5651 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5653 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5657 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5659 free(channel
->channel_info
->topic
);
5660 if(topic
[0] == '*' && topic
[1] == 0)
5662 topic
= channel
->channel_info
->topic
= NULL
;
5666 topic
= channel
->channel_info
->topic
= strdup(topic
);
5667 if(channel
->channel_info
->topic_mask
5668 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5669 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5671 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5674 if(channel
->channel_info
->topic
)
5675 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5677 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5681 static MODCMD_FUNC(chan_opt_topicmask
)
5685 struct chanData
*cData
= channel
->channel_info
;
5688 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5690 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5694 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5696 if(cData
->topic_mask
)
5697 free(cData
->topic_mask
);
5698 if(mask
[0] == '*' && mask
[1] == 0)
5700 cData
->topic_mask
= 0;
5704 cData
->topic_mask
= strdup(mask
);
5706 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5707 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5708 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5712 if(channel
->channel_info
->topic_mask
)
5713 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5715 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5719 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5723 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5727 if(greeting
[0] == '*' && greeting
[1] == 0)
5731 unsigned int length
= strlen(greeting
);
5732 if(length
> chanserv_conf
.greeting_length
)
5734 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5737 *data
= strdup(greeting
);
5746 reply(name
, user_find_message(user
, "MSG_NONE"));
5750 static MODCMD_FUNC(chan_opt_greeting
)
5752 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5755 static MODCMD_FUNC(chan_opt_usergreeting
)
5757 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5760 static MODCMD_FUNC(chan_opt_modes
)
5762 struct mod_chanmode
*new_modes
;
5763 char modes
[MODELEN
];
5767 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5768 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5772 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5774 reply("CSMSG_NO_ACCESS");
5777 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5779 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5781 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5783 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5786 else if(new_modes
->argc
> 1)
5788 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5789 mod_chanmode_free(new_modes
);
5794 channel
->channel_info
->modes
= *new_modes
;
5795 modcmd_chanmode_announce(new_modes
);
5796 mod_chanmode_free(new_modes
);
5800 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5802 reply("CSMSG_SET_MODES", modes
);
5804 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5808 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5810 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5812 struct chanData
*cData
= channel
->channel_info
;
5817 /* Set flag according to value. */
5818 if(enabled_string(argv
[1]))
5820 cData
->flags
|= mask
;
5823 else if(disabled_string(argv
[1]))
5825 cData
->flags
&= ~mask
;
5830 reply("MSG_INVALID_BINARY", argv
[1]);
5836 /* Find current option value. */
5837 value
= (cData
->flags
& mask
) ? 1 : 0;
5841 reply(name
, user_find_message(user
, "MSG_ON"));
5843 reply(name
, user_find_message(user
, "MSG_OFF"));
5847 static MODCMD_FUNC(chan_opt_nodelete
)
5849 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5851 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5855 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5858 static MODCMD_FUNC(chan_opt_dynlimit
)
5860 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5863 static MODCMD_FUNC(chan_opt_offchannel
)
5865 struct chanData
*cData
= channel
->channel_info
;
5870 /* Set flag according to value. */
5871 if(enabled_string(argv
[1]))
5873 if(!IsOffChannel(cData
))
5874 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5875 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5878 else if(disabled_string(argv
[1]))
5880 if(IsOffChannel(cData
))
5882 struct mod_chanmode change
;
5883 mod_chanmode_init(&change
);
5885 change
.args
[0].mode
= MODE_CHANOP
;
5886 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5887 mod_chanmode_announce(chanserv
, channel
, &change
);
5889 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5894 reply("MSG_INVALID_BINARY", argv
[1]);
5900 /* Find current option value. */
5901 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5905 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5907 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5911 static MODCMD_FUNC(chan_opt_defaults
)
5913 struct userData
*uData
;
5914 struct chanData
*cData
;
5915 const char *confirm
;
5916 enum levelOption lvlOpt
;
5917 enum charOption chOpt
;
5919 cData
= channel
->channel_info
;
5920 uData
= GetChannelUser(cData
, user
->handle_info
);
5921 if(!uData
|| (uData
->access
< UL_OWNER
))
5923 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5926 confirm
= make_confirmation_string(uData
);
5927 if((argc
< 2) || strcmp(argv
[1], confirm
))
5929 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5932 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5933 cData
->modes
= chanserv_conf
.default_modes
;
5934 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5935 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5936 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5937 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5938 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5943 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5945 struct chanData
*cData
= channel
->channel_info
;
5946 struct userData
*uData
;
5947 unsigned short value
;
5951 if(!check_user_level(channel
, user
, option
, 1, 1))
5953 reply("CSMSG_CANNOT_SET");
5956 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5957 if(!value
&& strcmp(argv
[1], "0"))
5959 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5962 uData
= GetChannelUser(cData
, user
->handle_info
);
5963 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5965 reply("CSMSG_BAD_SETLEVEL");
5971 /* This test only applies to owners, since non-owners
5972 * trying to set an option to above their level get caught
5973 * by the CSMSG_BAD_SETLEVEL test above.
5975 if(value
> uData
->access
)
5977 reply("CSMSG_BAD_SETTERS");
5984 cData
->lvlOpts
[option
] = value
;
5986 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5990 static MODCMD_FUNC(chan_opt_enfops
)
5992 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5995 static MODCMD_FUNC(chan_opt_enfhalfops
)
5997 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5999 static MODCMD_FUNC(chan_opt_enfmodes
)
6001 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6004 static MODCMD_FUNC(chan_opt_enftopic
)
6006 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6009 static MODCMD_FUNC(chan_opt_pubcmd
)
6011 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6014 static MODCMD_FUNC(chan_opt_setters
)
6016 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6019 static MODCMD_FUNC(chan_opt_userinfo
)
6021 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6024 static MODCMD_FUNC(chan_opt_topicsnarf
)
6026 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6029 static MODCMD_FUNC(chan_opt_inviteme
)
6031 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6034 /* TODO: Make look like this when no args are
6036 * -X3- -------------------------------
6037 * -X3- BanTimeout: Bans are removed:
6038 * -X3- ----- * indicates current -----
6039 * -X3- 0: [*] Never.
6040 * -X3- 1: [ ] After 10 minutes.
6041 * -X3- 2: [ ] After 2 hours.
6042 * -X3- 3: [ ] After 4 hours.
6043 * -X3- 4: [ ] After 24 hours.
6044 * -X3- 5: [ ] After one week.
6045 * -X3- ------------- End -------------
6048 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6050 struct chanData
*cData
= channel
->channel_info
;
6051 int count
= charOptions
[option
].count
, index
;
6055 index
= atoi(argv
[1]);
6057 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6059 reply("CSMSG_INVALID_NUMERIC", index
);
6060 /* Show possible values. */
6061 for(index
= 0; index
< count
; index
++)
6062 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6066 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6070 /* Find current option value. */
6073 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6077 /* Somehow, the option value is corrupt; reset it to the default. */
6078 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6083 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6087 static MODCMD_FUNC(chan_opt_automode
)
6089 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6092 static MODCMD_FUNC(chan_opt_protect
)
6094 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6097 static MODCMD_FUNC(chan_opt_toys
)
6099 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6102 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6104 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6107 static MODCMD_FUNC(chan_opt_bantimeout
)
6109 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6112 static MODCMD_FUNC(chan_opt_topicrefresh
)
6114 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6117 static MODCMD_FUNC(chan_opt_resync
)
6119 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6122 static struct svccmd_list set_shows_list
;
6125 handle_svccmd_unbind(struct svccmd
*target
) {
6127 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6128 if(target
== set_shows_list
.list
[ii
])
6129 set_shows_list
.used
= 0;
6132 static CHANSERV_FUNC(cmd_set
)
6134 struct svccmd
*subcmd
;
6138 /* Check if we need to (re-)initialize set_shows_list. */
6139 if(!set_shows_list
.used
)
6141 if(!set_shows_list
.size
)
6143 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6144 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6146 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6148 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6149 sprintf(buf
, "%s %s", argv
[0], name
);
6150 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6153 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6156 svccmd_list_append(&set_shows_list
, subcmd
);
6162 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6163 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6165 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6167 subcmd
= set_shows_list
.list
[ii
];
6168 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6170 reply("CSMSG_CHANNEL_OPTIONS_END");
6174 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6175 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6178 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6181 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6183 reply("CSMSG_NO_ACCESS");
6187 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6191 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6193 struct userData
*uData
;
6195 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6198 reply("CSMSG_NOT_USER", channel
->name
);
6204 /* Just show current option value. */
6206 else if(enabled_string(argv
[1]))
6208 uData
->flags
|= mask
;
6210 else if(disabled_string(argv
[1]))
6212 uData
->flags
&= ~mask
;
6216 reply("MSG_INVALID_BINARY", argv
[1]);
6220 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6224 static MODCMD_FUNC(user_opt_autoop
)
6226 struct userData
*uData
;
6228 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6231 reply("CSMSG_NOT_USER", channel
->name
);
6234 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6235 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6237 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6238 /* TODO: add halfops error message? or is the op one generic enough? */
6241 static MODCMD_FUNC(user_opt_autoinvite
)
6243 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6246 static MODCMD_FUNC(user_opt_info
)
6248 struct userData
*uData
;
6251 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6255 /* If they got past the command restrictions (which require access)
6256 * but fail this test, we have some fool with security override on.
6258 reply("CSMSG_NOT_USER", channel
->name
);
6265 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6266 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6268 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6271 bp
= strcspn(infoline
, "\001");
6274 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6279 if(infoline
[0] == '*' && infoline
[1] == 0)
6282 uData
->info
= strdup(infoline
);
6285 reply("CSMSG_USET_INFO", uData
->info
);
6287 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6291 struct svccmd_list uset_shows_list
;
6293 static CHANSERV_FUNC(cmd_uset
)
6295 struct svccmd
*subcmd
;
6299 /* Check if we need to (re-)initialize uset_shows_list. */
6300 if(!uset_shows_list
.used
)
6304 "AutoOp", "AutoInvite", "Info"
6307 if(!uset_shows_list
.size
)
6309 uset_shows_list
.size
= ArrayLength(options
);
6310 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6312 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6314 const char *name
= options
[ii
];
6315 sprintf(buf
, "%s %s", argv
[0], name
);
6316 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6319 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6322 svccmd_list_append(&uset_shows_list
, subcmd
);
6328 /* Do this so options are presented in a consistent order. */
6329 reply("CSMSG_USER_OPTIONS");
6330 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6331 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6335 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6336 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6339 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6343 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6346 static CHANSERV_FUNC(cmd_giveownership
)
6348 struct handle_info
*new_owner_hi
;
6349 struct userData
*new_owner
, *curr_user
;
6350 struct chanData
*cData
= channel
->channel_info
;
6351 struct do_not_register
*dnr
;
6352 struct giveownership
*giveownership
;
6353 unsigned int force
, override
;
6354 unsigned short co_access
, new_owner_old_access
;
6355 char transfer_reason
[MAXLEN
];
6358 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6359 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6361 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6362 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6363 && (uData
->access
> 500)
6364 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6365 || uData
->access
< 500));
6368 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6370 struct userData
*owner
= NULL
;
6371 for(curr_user
= channel
->channel_info
->users
;
6373 curr_user
= curr_user
->next
)
6375 if(curr_user
->access
!= UL_OWNER
)
6379 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6386 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6388 char delay
[INTERVALLEN
];
6389 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6390 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6393 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6395 if(new_owner_hi
== user
->handle_info
)
6397 reply("CSMSG_NO_TRANSFER_SELF");
6400 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6405 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6409 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6413 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6415 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6418 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6419 if(!IsHelping(user
))
6420 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6422 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6426 new_owner_old_access
= new_owner
->access
;
6427 if(new_owner
->access
>= UL_COOWNER
)
6428 co_access
= new_owner
->access
;
6430 co_access
= UL_COOWNER
;
6431 new_owner
->access
= UL_OWNER
;
6433 curr_user
->access
= co_access
;
6434 cData
->ownerTransfer
= now
;
6436 giveownership
= calloc(1, sizeof(*giveownership
));
6437 giveownership
->issued
= now
;
6438 giveownership
->old_owner
= curr_user
->handle
->handle
;
6439 giveownership
->target
= new_owner_hi
->handle
;
6440 giveownership
->target_access
= new_owner_old_access
;
6443 if(argc
> (2 + force
))
6445 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6446 giveownership
->reason
= strdup(transfer_reason
);
6448 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6451 giveownership
->previous
= channel
->channel_info
->giveownership
;
6452 channel
->channel_info
->giveownership
= giveownership
;
6454 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6455 global_message_args(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, "CSMSG_OWNERSHIP_TRANSFERRED",
6456 channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6461 chanserv_expire_user_suspension(void *data
)
6463 struct userData
*target
= data
;
6465 target
->expires
= 0;
6466 target
->flags
&= ~USER_SUSPENDED
;
6469 static CHANSERV_FUNC(cmd_suspend
)
6471 struct handle_info
*hi
;
6472 struct userData
*self
, *target
;
6476 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6477 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6478 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6480 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6483 if(target
->access
>= self
->access
)
6485 reply("MSG_USER_OUTRANKED", hi
->handle
);
6488 if(target
->flags
& USER_SUSPENDED
)
6490 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6495 target
->present
= 0;
6498 if(!strcmp(argv
[2], "0"))
6502 unsigned int duration
;
6503 if(!(duration
= ParseInterval(argv
[2])))
6505 reply("MSG_INVALID_DURATION", argv
[2]);
6508 expiry
= now
+ duration
;
6511 target
->expires
= expiry
;
6514 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6516 target
->flags
|= USER_SUSPENDED
;
6517 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6521 static CHANSERV_FUNC(cmd_unsuspend
)
6523 struct handle_info
*hi
;
6524 struct userData
*self
, *target
;
6527 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6528 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6529 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6531 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6534 if(target
->access
>= self
->access
)
6536 reply("MSG_USER_OUTRANKED", hi
->handle
);
6539 if(!(target
->flags
& USER_SUSPENDED
))
6541 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6544 target
->flags
&= ~USER_SUSPENDED
;
6545 scan_user_presence(target
, NULL
);
6546 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6547 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6551 static MODCMD_FUNC(cmd_deleteme
)
6553 struct handle_info
*hi
;
6554 struct userData
*target
;
6555 const char *confirm_string
;
6556 unsigned short access
;
6559 hi
= user
->handle_info
;
6560 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6562 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6565 if(target
->access
== UL_OWNER
)
6567 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6570 confirm_string
= make_confirmation_string(target
);
6571 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6573 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6576 access
= target
->access
;
6577 channel_name
= strdup(channel
->name
);
6578 del_channel_user(target
, 1);
6579 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6585 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6587 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6588 struct chanData
*cData
;
6591 for(cData
= channelList
; cData
; cData
= cData
->next
)
6593 if(IsSuspended(cData
))
6595 opt
= cData
->chOpts
[chTopicRefresh
];
6598 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6601 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6602 cData
->last_refresh
= refresh_num
;
6604 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6608 chanserv_auto_resync(UNUSED_ARG(void *data
))
6610 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6611 struct chanData
*cData
;
6614 for(cData
= channelList
; cData
; cData
= cData
->next
)
6616 if(IsSuspended(cData
)) continue;
6617 opt
= cData
->chOpts
[chResync
];
6618 if(opt
== 'n') continue;
6619 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6620 resync_channel(cData
->channel
);
6621 cData
->last_resync
= refresh_num
;
6623 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6626 static CHANSERV_FUNC(cmd_unf
)
6630 char response
[MAXLEN
];
6631 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6632 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6633 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6636 reply("CSMSG_UNF_RESPONSE");
6640 static CHANSERV_FUNC(cmd_ping
)
6644 char response
[MAXLEN
];
6645 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6646 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6647 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6650 reply("CSMSG_PING_RESPONSE");
6654 static CHANSERV_FUNC(cmd_wut
)
6658 char response
[MAXLEN
];
6659 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6660 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6661 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6664 reply("CSMSG_WUT_RESPONSE");
6669 static CHANSERV_FUNC(cmd_8ball
)
6671 unsigned int i
, j
, accum
;
6676 for(i
=1; i
<argc
; i
++)
6677 for(j
=0; argv
[i
][j
]; j
++)
6678 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6679 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6682 char response
[MAXLEN
];
6683 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6684 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6687 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6691 #else /* Use cool 8ball instead */
6693 void eightball(char *outcome
, int method
, unsigned int seed
)
6697 #define NUMOFCOLORS 18
6698 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6699 "white", "black", "grey", "brown",
6700 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6701 "fuchsia","turquoise","magenta", "cyan"};
6702 #define NUMOFLOCATIONS 50
6703 char balllocations
[50][55] = {
6704 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6705 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6706 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6707 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6708 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6709 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6710 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6711 "your bra", "your hair", "your bed", "the couch", "the wall",
6712 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6713 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6714 #define NUMOFPREPS 15
6715 char ballpreps
[50][50] = {
6716 "Near", "Somewhere near", "In", "In", "In",
6717 "In", "Hiding in", "Under", "Next to", "Over",
6718 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6719 #define NUMOFNUMS 34
6720 char ballnums
[50][50] = {
6721 "A hundred", "A thousand", "A few", "42",
6722 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6723 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6724 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6726 #define NUMOFMULTS 8
6727 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6730 * 0: normal (Not used in x3)
6737 if (method
== 1) /* A Color */
6741 answer
= (rand() % 12); /* Make sure this is the # of entries */
6744 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6746 case 1: strcpy(tmp
, "Sort of a light %s color.");
6748 case 2: strcpy(tmp
, "Dark and dreary %s.");
6750 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6752 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6754 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6756 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6758 case 10: strcpy(tmp
, "Solid %s.");
6760 case 11: strcpy(tmp
, "Transparent %s.");
6762 default: strcpy(outcome
, "An invalid random number was generated.");
6765 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6768 else if (method
== 2) /* Location */
6770 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6772 else if (method
== 3) /* Number of ___ */
6774 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6778 //Debug(DBGWARNING, "Error in 8ball.");
6783 static CHANSERV_FUNC(cmd_8ball
)
6785 char *word1
, *word2
, *word3
;
6786 static char eb
[MAXLEN
];
6787 unsigned int accum
, i
, j
;
6791 for(i
=1; i
<argc
; i
++)
6792 for(j
=0; argv
[i
][j
]; j
++)
6793 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6795 accum
+= time(NULL
)/3600;
6797 word2
= argc
>2?argv
[2]:"";
6798 word3
= argc
>3?argv
[3]:"";
6801 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6802 eightball(eb
, 1, accum
);
6803 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6804 eightball(eb
, 1, accum
);
6805 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6806 eightball(eb
, 1, accum
);
6807 /*** LOCATION *****/
6812 (strcasecmp(word1
, "where") == 0) &&
6813 (strcasecmp(word2
, "is") == 0)
6817 strcasecmp(word1
, "where's") == 0
6820 eightball(eb
, 2, accum
);
6822 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6823 eightball(eb
, 3, accum
);
6827 /* Generic 8ball question.. so pull from x3.conf srvx style */
6830 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6833 char response
[MAXLEN
];
6834 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6835 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6838 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6844 char response
[MAXLEN
];
6845 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6846 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6849 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6854 static CHANSERV_FUNC(cmd_d
)
6856 unsigned long sides
, count
, modifier
, ii
, total
;
6857 char response
[MAXLEN
], *sep
;
6861 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6871 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6872 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6876 else if((sep
[0] == '-') && isdigit(sep
[1]))
6877 modifier
= strtoul(sep
, NULL
, 10);
6878 else if((sep
[0] == '+') && isdigit(sep
[1]))
6879 modifier
= strtoul(sep
+1, NULL
, 10);
6886 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6891 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6894 for(total
= ii
= 0; ii
< count
; ++ii
)
6895 total
+= (rand() % sides
) + 1;
6898 if((count
> 1) || modifier
)
6900 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6901 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6905 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6906 sprintf(response
, fmt
, total
, sides
);
6909 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6911 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6915 static CHANSERV_FUNC(cmd_huggle
)
6917 /* CTCP must be via PRIVMSG, never notice */
6919 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6921 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6925 static CHANSERV_FUNC(cmd_calc
)
6927 char response
[MAXLEN
];
6930 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6933 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6935 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6940 chanserv_adjust_limit(void *data
)
6942 struct mod_chanmode change
;
6943 struct chanData
*cData
= data
;
6944 struct chanNode
*channel
= cData
->channel
;
6947 if(IsSuspended(cData
))
6950 cData
->limitAdjusted
= now
;
6951 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6952 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6954 if(limit
> cData
->modes
.new_limit
)
6955 limit
= cData
->modes
.new_limit
;
6956 else if(limit
== cData
->modes
.new_limit
)
6960 mod_chanmode_init(&change
);
6961 change
.modes_set
= MODE_LIMIT
;
6962 change
.new_limit
= limit
;
6963 mod_chanmode_announce(chanserv
, channel
, &change
);
6967 handle_new_channel(struct chanNode
*channel
)
6969 struct chanData
*cData
;
6971 if(!(cData
= channel
->channel_info
))
6974 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6975 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6977 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6978 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6981 /* Welcome to my worst nightmare. Warning: Read (or modify)
6982 the code below at your own risk. */
6984 handle_join(struct modeNode
*mNode
)
6986 struct mod_chanmode change
;
6987 struct userNode
*user
= mNode
->user
;
6988 struct chanNode
*channel
= mNode
->channel
;
6989 struct chanData
*cData
;
6990 struct userData
*uData
= NULL
;
6991 struct banData
*bData
;
6992 struct handle_info
*handle
;
6993 unsigned int modes
= 0, info
= 0;
6996 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6999 cData
= channel
->channel_info
;
7000 if(channel
->members
.used
> cData
->max
)
7001 cData
->max
= channel
->members
.used
;
7004 /* Check for bans. If they're joining through a ban, one of two
7006 * 1: Join during a netburst, by riding the break. Kick them
7007 * unless they have ops or voice in the channel.
7008 * 2: They're allowed to join through the ban (an invite in
7009 * ircu2.10, or a +e on Hybrid, or something).
7010 * If they're not joining through a ban, and the banlist is not
7011 * full, see if they're on the banlist for the channel. If so,
7014 if(user
->uplink
->burst
&& !mNode
->modes
)
7017 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7019 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7021 /* Riding a netburst. Naughty. */
7022 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7029 mod_chanmode_init(&change
);
7031 if(channel
->banlist
.used
< MAXBANS
)
7033 /* Not joining through a ban. */
7034 for(bData
= cData
->bans
;
7035 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7036 bData
= bData
->next
);
7040 char kick_reason
[MAXLEN
];
7041 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7043 bData
->triggered
= now
;
7044 if(bData
!= cData
->bans
)
7046 /* Shuffle the ban to the head of the list. */
7048 bData
->next
->prev
= bData
->prev
;
7050 bData
->prev
->next
= bData
->next
;
7053 bData
->next
= cData
->bans
;
7056 cData
->bans
->prev
= bData
;
7057 cData
->bans
= bData
;
7060 change
.args
[0].mode
= MODE_BAN
;
7061 change
.args
[0].u
.hostmask
= bData
->mask
;
7062 mod_chanmode_announce(chanserv
, channel
, &change
);
7063 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7068 /* ChanServ will not modify the limits in join-flooded channels.
7069 It will also skip DynLimit processing when the user (or srvx)
7070 is bursting in, because there are likely more incoming. */
7071 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7072 && !user
->uplink
->burst
7073 && !channel
->join_flooded
7074 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7076 /* The user count has begun "bumping" into the channel limit,
7077 so set a timer to raise the limit a bit. Any previous
7078 timers are removed so three incoming users within the delay
7079 results in one limit change, not three. */
7081 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7082 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7085 /* Give automodes exept during join-floods */
7086 if(!channel
->join_flooded
)
7088 if(cData
->chOpts
[chAutomode
] == 'v')
7089 modes
|= MODE_VOICE
;
7090 else if(cData
->chOpts
[chAutomode
] == 'h')
7091 modes
|= MODE_HALFOP
;
7092 else if(cData
->chOpts
[chAutomode
] == 'o')
7093 modes
|= MODE_CHANOP
;
7096 greeting
= cData
->greeting
;
7097 if(user
->handle_info
)
7099 handle
= user
->handle_info
;
7101 if(IsHelper(user
) && !IsHelping(user
))
7104 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7106 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7108 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7114 uData
= GetTrueChannelAccess(cData
, handle
);
7115 if(uData
&& !IsUserSuspended(uData
))
7117 /* non users getting automodes are handled above. */
7118 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7120 /* just op everyone with access */
7121 if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] == 'l')
7122 modes
|= MODE_VOICE
;
7123 /* or do their access level */
7124 else if(uData
->access
>= UL_OP
)
7125 modes
|= MODE_CHANOP
;
7126 else if(uData
->access
>= UL_HALFOP
)
7127 modes
|= MODE_HALFOP
;
7128 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7129 modes
|= MODE_VOICE
;
7131 if(uData
->access
>= UL_PRESENT
)
7132 cData
->visited
= now
;
7133 if(cData
->user_greeting
)
7134 greeting
= cData
->user_greeting
;
7136 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7137 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7145 /* If user joining normally (not during burst), apply op or voice,
7146 * and send greeting/userinfo as appropriate.
7148 if(!user
->uplink
->burst
)
7152 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7153 if(modes & MODE_CHANOP) {
7154 modes &= ~MODE_HALFOP;
7155 modes &= ~MODE_VOICE;
7158 change
.args
[0].mode
= modes
;
7159 change
.args
[0].u
.member
= mNode
;
7160 mod_chanmode_announce(chanserv
, channel
, &change
);
7163 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7165 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7171 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7173 struct mod_chanmode change
;
7174 struct userData
*channel
;
7175 unsigned int ii
, jj
, i
;
7177 if(!user
->handle_info
)
7180 mod_chanmode_init(&change
);
7182 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7184 struct chanNode
*cn
;
7185 struct modeNode
*mn
;
7186 if(IsUserSuspended(channel
)
7187 || IsSuspended(channel
->channel
)
7188 || !(cn
= channel
->channel
->channel
))
7191 mn
= GetUserMode(cn
, user
);
7194 if(!IsUserSuspended(channel
)
7195 && IsUserAutoInvite(channel
)
7196 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7198 && !user
->uplink
->burst
)
7199 irc_invite(chanserv
, user
, cn
);
7203 if(channel
->access
>= UL_PRESENT
)
7204 channel
->channel
->visited
= now
;
7206 if(IsUserAutoOp(channel
))
7208 if(channel
->access
>= UL_OP
)
7209 change
.args
[0].mode
= MODE_CHANOP
;
7210 else if(channel
->access
>= UL_HALFOP
)
7211 change
.args
[0].mode
= MODE_HALFOP
;
7212 else if(channel
->access
>= UL_PEON
)
7213 change
.args
[0].mode
= MODE_VOICE
;
7215 change
.args
[0].mode
= 0;
7216 change
.args
[0].u
.member
= mn
;
7217 if(change
.args
[0].mode
)
7218 mod_chanmode_announce(chanserv
, cn
, &change
);
7221 channel
->seen
= now
;
7222 channel
->present
= 1;
7225 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7227 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7228 struct banData
*ban
;
7230 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7231 || !channel
->channel_info
7232 || IsSuspended(channel
->channel_info
))
7234 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7235 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7237 if(jj
< channel
->banlist
.used
)
7239 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7241 char kick_reason
[MAXLEN
];
7242 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7244 change
.args
[0].mode
= MODE_BAN
;
7245 change
.args
[0].u
.hostmask
= ban
->mask
;
7246 mod_chanmode_announce(chanserv
, channel
, &change
);
7247 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7248 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7249 ban
->triggered
= now
;
7254 if(IsSupportHelper(user
))
7256 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7258 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7260 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7266 if (user
->handle_info
->ignores
->used
) {
7267 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7268 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7272 if (user
->handle_info
->epithet
)
7273 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7277 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7279 struct chanData
*cData
;
7280 struct userData
*uData
;
7282 cData
= mn
->channel
->channel_info
;
7283 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7286 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7288 /* Allow for a bit of padding so that the limit doesn't
7289 track the user count exactly, which could get annoying. */
7290 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7292 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7293 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7297 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7299 scan_user_presence(uData
, mn
->user
);
7303 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7305 unsigned int ii
, jj
;
7306 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7308 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7309 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7311 if(jj
< mn
->user
->channels
.used
)
7314 if(ii
== chanserv_conf
.support_channels
.used
)
7315 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7320 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7322 struct userData
*uData
;
7324 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7325 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7326 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7329 if(protect_user(victim
, kicker
, channel
->channel_info
))
7331 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7332 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7335 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7340 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7342 struct chanData
*cData
;
7344 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7347 cData
= channel
->channel_info
;
7348 if(bad_topic(channel
, user
, channel
->topic
))
7349 { /* User doesnt have privs to set topics. Undo it */
7350 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7351 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7354 /* If there is a topic mask set, and the new topic doesnt match,
7355 * set the topic to mask + new_topic */
7356 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7358 char new_topic
[TOPICLEN
+1];
7359 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7362 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7363 /* and fall through to topicsnarf code below.. */
7365 else /* Topic couldnt fit into mask, was too long */
7367 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7368 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7369 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7373 /* With topicsnarf, grab the topic and save it as the default topic. */
7374 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7377 cData
->topic
= strdup(channel
->topic
);
7383 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7385 struct mod_chanmode
*bounce
= NULL
;
7386 unsigned int bnc
, ii
;
7389 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7392 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7393 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7395 char correct
[MAXLEN
];
7396 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7397 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7398 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7400 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7402 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7404 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7405 if(!protect_user(victim
, user
, channel
->channel_info
))
7408 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7411 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7412 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7413 if(bounce
->args
[bnc
].u
.member
)
7417 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7418 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7420 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7422 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7424 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7425 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7428 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7429 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7430 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7433 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7435 const char *ban
= change
->args
[ii
].u
.hostmask
;
7436 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7439 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7440 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7441 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7443 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7448 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7449 mod_chanmode_announce(chanserv
, channel
, bounce
);
7450 for(ii
= 0; ii
< change
->argc
; ++ii
)
7451 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7452 free((char*)bounce
->args
[ii
].u
.hostmask
);
7453 mod_chanmode_free(bounce
);
7458 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7460 struct chanNode
*channel
;
7461 struct banData
*bData
;
7462 struct mod_chanmode change
;
7463 unsigned int ii
, jj
;
7464 char kick_reason
[MAXLEN
];
7466 mod_chanmode_init(&change
);
7468 change
.args
[0].mode
= MODE_BAN
;
7469 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7471 channel
= user
->channels
.list
[ii
]->channel
;
7472 /* Need not check for bans if they're opped or voiced. */
7473 /* TODO: does this make sense in automode v, h, and o? *
7474 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7475 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7477 /* Need not check for bans unless channel registration is active. */
7478 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7480 /* Look for a matching ban already on the channel. */
7481 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7482 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7484 /* Need not act if we found one. */
7485 if(jj
< channel
->banlist
.used
)
7487 /* Look for a matching ban in this channel. */
7488 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7490 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7492 change
.args
[0].u
.hostmask
= bData
->mask
;
7493 mod_chanmode_announce(chanserv
, channel
, &change
);
7494 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7495 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7496 bData
->triggered
= now
;
7497 break; /* we don't need to check any more bans in the channel */
7502 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7504 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7508 dict_remove2(handle_dnrs
, old_handle
, 1);
7509 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7510 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7515 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7517 struct userNode
*h_user
;
7519 if(handle
->channels
)
7521 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7522 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7524 while(handle
->channels
)
7525 del_channel_user(handle
->channels
, 1);
7530 handle_server_link(UNUSED_ARG(struct server
*server
))
7532 struct chanData
*cData
;
7534 for(cData
= channelList
; cData
; cData
= cData
->next
)
7536 if(!IsSuspended(cData
))
7537 cData
->may_opchan
= 1;
7538 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7539 && !cData
->channel
->join_flooded
7540 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7541 < chanserv_conf
.adjust_threshold
))
7543 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7544 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7550 chanserv_conf_read(void)
7554 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7555 struct mod_chanmode
*change
;
7556 struct string_list
*strlist
;
7557 struct chanNode
*chan
;
7560 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7562 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7565 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7566 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7567 chanserv_conf
.support_channels
.used
= 0;
7568 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7570 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7572 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7575 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7577 channelList_append(&chanserv_conf
.support_channels
, chan
);
7580 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7583 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7586 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7588 channelList_append(&chanserv_conf
.support_channels
, chan
);
7590 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7591 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7592 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7593 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7594 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7595 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7596 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7597 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7598 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7599 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7600 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7601 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7602 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7603 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7604 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7605 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7606 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7607 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7608 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7609 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7610 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7611 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7612 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7613 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7614 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7616 NickChange(chanserv
, str
, 0);
7617 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7618 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7619 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7620 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7621 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7622 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7623 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7624 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7625 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7626 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7627 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7628 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7629 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7630 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7631 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7632 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7633 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7634 god_timeout
= str
? ParseInterval(str
) : 60*15;
7635 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7638 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7639 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7640 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7641 && (change
->argc
< 2))
7643 chanserv_conf
.default_modes
= *change
;
7644 mod_chanmode_free(change
);
7646 free_string_list(chanserv_conf
.set_shows
);
7647 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7649 strlist
= string_list_copy(strlist
);
7652 static const char *list
[] = {
7653 /* free form text */
7654 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7655 /* options based on user level */
7656 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7657 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7658 /* multiple choice options */
7659 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7660 /* binary options */
7661 "DynLimit", "NoDelete", "BanTimeout",
7666 strlist
= alloc_string_list(ArrayLength(list
)-1);
7667 for(ii
=0; list
[ii
]; ii
++)
7668 string_list_append(strlist
, strdup(list
[ii
]));
7670 chanserv_conf
.set_shows
= strlist
;
7671 /* We don't look things up now, in case the list refers to options
7672 * defined by modules initialized after this point. Just mark the
7673 * function list as invalid, so it will be initialized.
7675 set_shows_list
.used
= 0;
7676 free_string_list(chanserv_conf
.eightball
);
7677 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7680 strlist
= string_list_copy(strlist
);
7684 strlist
= alloc_string_list(4);
7685 string_list_append(strlist
, strdup("Yes."));
7686 string_list_append(strlist
, strdup("No."));
7687 string_list_append(strlist
, strdup("Maybe so."));
7689 chanserv_conf
.eightball
= strlist
;
7690 free_string_list(chanserv_conf
.old_ban_names
);
7691 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7693 strlist
= string_list_copy(strlist
);
7695 strlist
= alloc_string_list(2);
7696 chanserv_conf
.old_ban_names
= strlist
;
7697 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7698 off_channel
= str
? atoi(str
) : 0;
7702 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7705 struct note_type
*ntype
;
7708 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7710 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7713 if(!(ntype
= chanserv_create_note_type(key
)))
7715 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7719 /* Figure out set access */
7720 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7722 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7723 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7725 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7727 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7728 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7730 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7732 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7736 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7737 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7738 ntype
->set_access
.min_opserv
= 0;
7741 /* Figure out visibility */
7742 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7743 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7744 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7745 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7746 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7747 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7748 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7749 ntype
->visible_type
= NOTE_VIS_ALL
;
7751 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7753 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7754 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7758 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7760 struct handle_info
*handle
;
7761 struct userData
*uData
;
7762 char *seen
, *inf
, *flags
, *expires
;
7764 unsigned short access
;
7766 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7768 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7772 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7773 if(access
> UL_OWNER
)
7775 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7779 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7780 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7781 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7782 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7783 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7784 handle
= get_handle_info(key
);
7787 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7791 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7792 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7793 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7795 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7797 if(uData
->expires
> now
)
7798 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7800 uData
->flags
&= ~USER_SUSPENDED
;
7803 /* Upgrade: set autoop to the inverse of noautoop */
7804 if(chanserv_read_version
< 2)
7806 /* if noautoop is true, set autoop false, and vice versa */
7807 if(uData
->flags
& USER_NOAUTO_OP
)
7808 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7810 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7811 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
);
7817 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7819 struct banData
*bData
;
7820 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7821 time_t set_time
, triggered_time
, expires_time
;
7823 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7825 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7829 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7830 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7831 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7832 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7833 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7834 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7835 if (!reason
|| !owner
)
7838 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7839 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7841 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7843 expires_time
= set_time
+ atoi(s_duration
);
7847 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7850 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7853 static struct suspended
*
7854 chanserv_read_suspended(dict_t obj
)
7856 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7860 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7861 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7862 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7863 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7864 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7865 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7866 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7867 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7868 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7869 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7873 static struct giveownership
*
7874 chanserv_read_giveownership(dict_t obj
)
7876 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7880 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7881 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7883 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7885 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7886 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7888 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7889 giveownership
->reason
= str
? strdup(str
) : NULL
;
7890 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7891 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7893 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7894 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7895 return giveownership
;
7899 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7901 struct suspended
*suspended
;
7902 struct giveownership
*giveownership
;
7903 struct mod_chanmode
*modes
;
7904 struct chanNode
*cNode
;
7905 struct chanData
*cData
;
7906 struct dict
*channel
, *obj
;
7907 char *str
, *argv
[10];
7911 channel
= hir
->d
.object
;
7913 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7916 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7919 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7922 cData
= register_channel(cNode
, str
);
7925 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7929 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7931 enum levelOption lvlOpt
;
7932 enum charOption chOpt
;
7934 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7935 cData
->flags
= atoi(str
);
7937 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7939 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7941 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7942 else if(levelOptions
[lvlOpt
].old_flag
)
7944 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7945 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7947 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7951 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7953 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7955 cData
->chOpts
[chOpt
] = str
[0];
7958 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7960 enum levelOption lvlOpt
;
7961 enum charOption chOpt
;
7964 cData
->flags
= base64toint(str
, 5);
7965 count
= strlen(str
+= 5);
7966 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7969 if(levelOptions
[lvlOpt
].old_flag
)
7971 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7972 lvl
= levelOptions
[lvlOpt
].flag_value
;
7974 lvl
= levelOptions
[lvlOpt
].default_value
;
7976 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7978 case 'c': lvl
= UL_COOWNER
; break;
7979 case 'm': lvl
= UL_MANAGER
; break;
7980 case 'n': lvl
= UL_OWNER
+1; break;
7981 case 'o': lvl
= UL_OP
; break;
7982 case 'p': lvl
= UL_PEON
; break;
7983 case 'h': lvl
= UL_HALFOP
; break;
7984 case 'w': lvl
= UL_OWNER
; break;
7985 default: lvl
= 0; break;
7987 cData
->lvlOpts
[lvlOpt
] = lvl
;
7989 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7990 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7993 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7995 suspended
= chanserv_read_suspended(obj
);
7996 cData
->suspended
= suspended
;
7997 suspended
->cData
= cData
;
7998 /* We could use suspended->expires and suspended->revoked to
7999 * set the CHANNEL_SUSPENDED flag, but we don't. */
8001 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
8003 suspended
= calloc(1, sizeof(*suspended
));
8004 suspended
->issued
= 0;
8005 suspended
->revoked
= 0;
8006 suspended
->suspender
= strdup(str
);
8007 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8008 suspended
->expires
= str
? atoi(str
) : 0;
8009 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8010 suspended
->reason
= strdup(str
? str
: "No reason");
8011 suspended
->previous
= NULL
;
8012 cData
->suspended
= suspended
;
8013 suspended
->cData
= cData
;
8017 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8018 suspended
= NULL
; /* to squelch a warning */
8021 if(IsSuspended(cData
)) {
8022 if(suspended
->expires
> now
)
8023 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8024 else if(suspended
->expires
)
8025 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8028 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8030 giveownership
= chanserv_read_giveownership(obj
);
8031 cData
->giveownership
= giveownership
;
8034 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8035 struct mod_chanmode change
;
8036 mod_chanmode_init(&change
);
8038 change
.args
[0].mode
= MODE_CHANOP
;
8039 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8040 mod_chanmode_announce(chanserv
, cNode
, &change
);
8043 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8044 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8045 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8046 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8047 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8048 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8049 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8050 cData
->max
= str
? atoi(str
) : 0;
8051 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8052 cData
->greeting
= str
? strdup(str
) : NULL
;
8053 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8054 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8055 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8056 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8057 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8058 cData
->topic
= str
? strdup(str
) : NULL
;
8060 if(!IsSuspended(cData
)
8061 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8062 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8063 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8064 cData
->modes
= *modes
;
8066 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8067 if(cData
->modes
.argc
> 1)
8068 cData
->modes
.argc
= 1;
8069 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8070 mod_chanmode_free(modes
);
8073 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8074 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8075 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8077 if(!cData
->users
&& !IsProtected(cData
))
8079 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8080 unregister_channel(cData
, "has empty user list.");
8084 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8085 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8086 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8088 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8089 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8091 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8092 struct record_data
*rd
= iter_data(it
);
8093 const char *note
, *setter
;
8095 if(rd
->type
!= RECDB_OBJECT
)
8097 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8101 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8103 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8105 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8109 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8110 if(!setter
) setter
= "<unknown>";
8111 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8119 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8121 const char *setter
, *reason
, *str
;
8122 struct do_not_register
*dnr
;
8124 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8127 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8130 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8133 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8136 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8139 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8141 dnr
->set
= atoi(str
);
8147 chanserv_version_read(struct dict
*section
)
8151 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8153 chanserv_read_version
= atoi(str
);
8154 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8158 chanserv_saxdb_read(struct dict
*database
)
8160 struct dict
*section
;
8163 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8164 chanserv_version_read(section
);
8166 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8167 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8168 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8170 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8171 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8172 chanserv_channel_read(iter_key(it
), iter_data(it
));
8174 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8175 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8176 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8182 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8184 int high_present
= 0;
8185 saxdb_start_record(ctx
, KEY_USERS
, 1);
8186 for(; uData
; uData
= uData
->next
)
8188 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8190 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8191 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8192 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8194 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8196 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8198 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8199 saxdb_end_record(ctx
);
8201 saxdb_end_record(ctx
);
8202 return high_present
;
8206 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8210 saxdb_start_record(ctx
, KEY_BANS
, 1);
8211 for(; bData
; bData
= bData
->next
)
8213 saxdb_start_record(ctx
, bData
->mask
, 0);
8214 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8215 if(bData
->triggered
)
8216 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8218 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8220 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8222 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8223 saxdb_end_record(ctx
);
8225 saxdb_end_record(ctx
);
8229 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8231 saxdb_start_record(ctx
, name
, 0);
8232 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8233 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8235 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8237 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8239 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8241 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8242 saxdb_end_record(ctx
);
8246 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8248 saxdb_start_record(ctx
, name
, 0);
8249 if(giveownership
->staff_issuer
)
8250 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8251 if(giveownership
->old_owner
)
8252 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8253 if(giveownership
->target
)
8254 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8255 if(giveownership
->target_access
)
8256 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8257 if(giveownership
->reason
)
8258 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8259 if(giveownership
->issued
)
8260 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8261 if(giveownership
->previous
)
8262 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8263 saxdb_end_record(ctx
);
8267 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8271 enum levelOption lvlOpt
;
8272 enum charOption chOpt
;
8274 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8276 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8277 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8279 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8280 if(channel
->registrar
)
8281 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8282 if(channel
->greeting
)
8283 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8284 if(channel
->user_greeting
)
8285 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8286 if(channel
->topic_mask
)
8287 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8288 if(channel
->suspended
)
8289 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8290 if(channel
->giveownership
)
8291 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8293 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8294 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8295 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8296 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8297 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8299 buf
[0] = channel
->chOpts
[chOpt
];
8301 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8303 saxdb_end_record(ctx
);
8305 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8307 mod_chanmode_format(&channel
->modes
, buf
);
8308 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8311 high_present
= chanserv_write_users(ctx
, channel
->users
);
8312 chanserv_write_bans(ctx
, channel
->bans
);
8314 if(dict_size(channel
->notes
))
8318 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8319 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8321 struct note
*note
= iter_data(it
);
8322 saxdb_start_record(ctx
, iter_key(it
), 0);
8323 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8324 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8325 saxdb_end_record(ctx
);
8327 saxdb_end_record(ctx
);
8330 if(channel
->ownerTransfer
)
8331 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8332 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8333 saxdb_end_record(ctx
);
8337 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8341 saxdb_start_record(ctx
, ntype
->name
, 0);
8342 switch(ntype
->set_access_type
)
8344 case NOTE_SET_CHANNEL_ACCESS
:
8345 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8347 case NOTE_SET_CHANNEL_SETTER
:
8348 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8350 case NOTE_SET_PRIVILEGED
: default:
8351 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8354 switch(ntype
->visible_type
)
8356 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8357 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8358 case NOTE_VIS_PRIVILEGED
:
8359 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8361 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8362 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8363 saxdb_end_record(ctx
);
8367 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8369 struct do_not_register
*dnr
;
8372 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8374 dnr
= iter_data(it
);
8375 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8377 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8378 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8379 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8380 saxdb_end_record(ctx
);
8385 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8388 struct chanData
*channel
;
8390 /* Version Control*/
8391 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8392 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8393 saxdb_end_record(ctx
);
8396 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8397 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8398 chanserv_write_note_type(ctx
, iter_data(it
));
8399 saxdb_end_record(ctx
);
8402 saxdb_start_record(ctx
, KEY_DNR
, 1);
8403 write_dnrs_helper(ctx
, handle_dnrs
);
8404 write_dnrs_helper(ctx
, plain_dnrs
);
8405 write_dnrs_helper(ctx
, mask_dnrs
);
8406 saxdb_end_record(ctx
);
8409 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8410 for(channel
= channelList
; channel
; channel
= channel
->next
)
8411 chanserv_write_channel(ctx
, channel
);
8412 saxdb_end_record(ctx
);
8418 chanserv_db_cleanup(void) {
8420 unreg_part_func(handle_part
);
8422 unregister_channel(channelList
, "terminating.");
8423 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8424 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8425 free(chanserv_conf
.support_channels
.list
);
8426 dict_delete(handle_dnrs
);
8427 dict_delete(plain_dnrs
);
8428 dict_delete(mask_dnrs
);
8429 dict_delete(note_types
);
8430 free_string_list(chanserv_conf
.eightball
);
8431 free_string_list(chanserv_conf
.old_ban_names
);
8432 free_string_list(chanserv_conf
.set_shows
);
8433 free(set_shows_list
.list
);
8434 free(uset_shows_list
.list
);
8437 struct userData
*helper
= helperList
;
8438 helperList
= helperList
->next
;
8443 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8444 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8445 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8448 init_chanserv(const char *nick
)
8450 struct chanNode
*chan
;
8452 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8453 conf_register_reload(chanserv_conf_read
);
8455 reg_server_link_func(handle_server_link
);
8457 reg_new_channel_func(handle_new_channel
);
8458 reg_join_func(handle_join
);
8459 reg_part_func(handle_part
);
8460 reg_kick_func(handle_kick
);
8461 reg_topic_func(handle_topic
);
8462 reg_mode_change_func(handle_mode
);
8463 reg_nick_change_func(handle_nick_change
);
8465 reg_auth_func(handle_auth
);
8466 reg_handle_rename_func(handle_rename
);
8467 reg_unreg_func(handle_unreg
);
8469 handle_dnrs
= dict_new();
8470 dict_set_free_data(handle_dnrs
, free
);
8471 plain_dnrs
= dict_new();
8472 dict_set_free_data(plain_dnrs
, free
);
8473 mask_dnrs
= dict_new();
8474 dict_set_free_data(mask_dnrs
, free
);
8476 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8477 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8478 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8479 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8480 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8481 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8482 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8483 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8484 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8485 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8487 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8489 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8490 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8492 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8493 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8494 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8495 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8496 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8498 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8499 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8500 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8501 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8502 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8503 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8505 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8506 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8507 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8508 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8510 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8511 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8512 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8513 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8514 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8515 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8516 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8517 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8518 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8519 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8521 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8522 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8523 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8524 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8525 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8526 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8527 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8528 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8529 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8530 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8531 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8532 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8533 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8534 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8536 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8537 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8538 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8539 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8541 /* if you change dellamer access, see also places
8542 * like unbanme which have manager hardcoded. */
8543 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8544 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8546 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8548 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8550 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8551 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8552 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8553 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8554 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8555 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8556 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8557 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8558 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8559 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8560 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8561 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8563 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8564 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8566 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8567 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8568 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8569 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8571 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8572 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8573 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8574 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8575 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8577 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8578 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8579 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8580 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8581 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8582 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8583 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8585 /* Channel options */
8586 DEFINE_CHANNEL_OPTION(defaulttopic
);
8587 DEFINE_CHANNEL_OPTION(topicmask
);
8588 DEFINE_CHANNEL_OPTION(greeting
);
8589 DEFINE_CHANNEL_OPTION(usergreeting
);
8590 DEFINE_CHANNEL_OPTION(modes
);
8591 DEFINE_CHANNEL_OPTION(enfops
);
8592 DEFINE_CHANNEL_OPTION(enfhalfops
);
8593 DEFINE_CHANNEL_OPTION(automode
);
8594 DEFINE_CHANNEL_OPTION(protect
);
8595 DEFINE_CHANNEL_OPTION(enfmodes
);
8596 DEFINE_CHANNEL_OPTION(enftopic
);
8597 DEFINE_CHANNEL_OPTION(pubcmd
);
8598 DEFINE_CHANNEL_OPTION(userinfo
);
8599 DEFINE_CHANNEL_OPTION(dynlimit
);
8600 DEFINE_CHANNEL_OPTION(topicsnarf
);
8601 DEFINE_CHANNEL_OPTION(nodelete
);
8602 DEFINE_CHANNEL_OPTION(toys
);
8603 DEFINE_CHANNEL_OPTION(setters
);
8604 DEFINE_CHANNEL_OPTION(topicrefresh
);
8605 DEFINE_CHANNEL_OPTION(resync
);
8606 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8607 DEFINE_CHANNEL_OPTION(bantimeout
);
8608 DEFINE_CHANNEL_OPTION(inviteme
);
8610 DEFINE_CHANNEL_OPTION(offchannel
);
8611 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8613 /* Alias set topic to set defaulttopic for compatibility. */
8614 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8617 DEFINE_USER_OPTION(autoinvite
);
8618 DEFINE_USER_OPTION(info
);
8619 DEFINE_USER_OPTION(autoop
);
8621 /* Alias uset autovoice to uset autoop. */
8622 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8624 note_types
= dict_new();
8625 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8628 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8629 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8630 service_register(chanserv
)->trigger
= '!';
8631 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8634 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8636 if(chanserv_conf
.channel_expire_frequency
)
8637 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8639 if(chanserv_conf
.ban_timeout_frequency
)
8640 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8642 if(chanserv_conf
.refresh_period
)
8644 time_t next_refresh
;
8645 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8646 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8647 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8650 if (autojoin_channels
&& chanserv
) {
8651 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8652 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8653 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8657 reg_exit_func(chanserv_db_cleanup
);
8658 message_register_table(msgtab
);