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." },
320 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
321 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
322 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
323 { "CSMSG_PROTECT_NONE", "No users will be protected." },
324 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
325 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
326 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
328 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
329 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
330 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
331 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
332 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
334 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
335 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
336 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
337 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
338 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
340 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
341 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
342 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
343 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
344 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
346 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
347 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
348 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
349 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
350 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
351 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
353 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
354 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
355 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
356 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
357 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
358 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
359 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
360 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
361 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
363 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
364 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
365 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
367 /* Channel userlist */
368 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
369 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
370 /* uncomment if needed to adujust styles (and change code below)
371 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
372 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
373 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
374 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
375 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
376 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
378 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
379 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
380 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
382 /* Channel note list */
383 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
384 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
385 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
386 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
387 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
388 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
389 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
390 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
391 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
392 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
393 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
394 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
395 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
396 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
397 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
399 /* Channel [un]suspension */
400 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
401 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
402 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
403 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
404 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
405 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
406 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
408 /* Access information */
409 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
410 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
411 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
412 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
413 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
414 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
415 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
416 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
417 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
418 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
419 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
421 /* Seen information */
422 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
423 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
424 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
425 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
427 /* Names information */
428 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
429 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
431 /* Channel information */
432 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
433 { "CSMSG_BAR", "----------------------------------------"},
434 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
435 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
436 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
437 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
438 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
439 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
440 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
441 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
442 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
443 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
444 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
445 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
446 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
447 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
452 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
453 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
454 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
455 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
456 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
457 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
458 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
459 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
461 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
462 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
463 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
464 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
465 { "CSMSG_PEEK_OPS", "$bOps:$b" },
466 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
467 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
469 /* Network information */
470 { "CSMSG_NETWORK_INFO", "Network Information:" },
471 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
472 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
473 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
474 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
475 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
476 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
477 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
478 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
481 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
482 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
483 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
485 /* Channel searches */
486 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
487 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
488 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
489 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
491 /* Channel configuration */
492 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
493 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
494 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
495 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
498 { "CSMSG_USER_OPTIONS", "User Options:" },
499 // { "CSMSG_USER_PROTECTED", "That user is protected." },
502 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
503 { "CSMSG_PING_RESPONSE", "Pong!" },
504 { "CSMSG_WUT_RESPONSE", "wut" },
505 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
506 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
507 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
508 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
509 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
510 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
511 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
514 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
515 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
516 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
517 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
521 /* eject_user and unban_user flags */
522 #define ACTION_KICK 0x0001
523 #define ACTION_BAN 0x0002
524 #define ACTION_ADD_LAMER 0x0004
525 #define ACTION_ADD_TIMED_LAMER 0x0008
526 #define ACTION_UNBAN 0x0010
527 #define ACTION_DEL_LAMER 0x0020
529 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
530 #define MODELEN 40 + KEYLEN
534 #define CSFUNC_ARGS user, channel, argc, argv, cmd
536 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
537 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
538 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
539 reply("MSG_MISSING_PARAMS", argv[0]); \
543 DECLARE_LIST(dnrList
, struct do_not_register
*);
544 DEFINE_LIST(dnrList
, struct do_not_register
*);
546 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
548 struct userNode
*chanserv
;
551 extern struct string_list
*autojoin_channels
;
552 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
553 static struct log_type
*CS_LOG
;
554 struct adduserPending
* adduser_pendings
= NULL
;
555 unsigned int adduser_pendings_count
= 0;
556 unsigned long god_timeout
;
560 struct channelList support_channels
;
561 struct mod_chanmode default_modes
;
563 unsigned long db_backup_frequency
;
564 unsigned long channel_expire_frequency
;
565 unsigned long ban_timeout_frequency
;
568 unsigned int adjust_delay
;
569 long channel_expire_delay
;
570 unsigned int nodelete_level
;
572 unsigned int adjust_threshold
;
573 int join_flood_threshold
;
575 unsigned int greeting_length
;
576 unsigned int refresh_period
;
577 unsigned int giveownership_period
;
579 unsigned int max_owned
;
580 unsigned int max_chan_users
;
581 unsigned int max_chan_bans
; /* lamers */
582 unsigned int max_userinfo_length
;
584 struct string_list
*set_shows
;
585 struct string_list
*eightball
;
586 struct string_list
*old_ban_names
;
588 const char *ctcp_short_ban_duration
;
589 const char *ctcp_long_ban_duration
;
591 const char *irc_operator_epithet
;
592 const char *network_helper_epithet
;
593 const char *support_helper_epithet
;
598 struct userNode
*user
;
599 struct userNode
*bot
;
600 struct chanNode
*channel
;
602 unsigned short lowest
;
603 unsigned short highest
;
604 struct userData
**users
;
605 struct helpfile_table table
;
608 enum note_access_type
610 NOTE_SET_CHANNEL_ACCESS
,
611 NOTE_SET_CHANNEL_SETTER
,
615 enum note_visible_type
618 NOTE_VIS_CHANNEL_USERS
,
624 enum note_access_type set_access_type
;
626 unsigned int min_opserv
;
627 unsigned short min_ulevel
;
629 enum note_visible_type visible_type
;
630 unsigned int max_length
;
637 struct note_type
*type
;
638 char setter
[NICKSERV_HANDLE_LEN
+1];
642 static unsigned int registered_channels
;
643 static unsigned int banCount
;
645 static const struct {
648 unsigned short level
;
650 } accessLevels
[] = { /* MUST be orderd less to most! */
651 { "peon", "Peon", UL_PEON
, '+' },
652 { "halfop", "HalfOp", UL_HALFOP
, '%' },
653 { "op", "Op", UL_OP
, '@' },
654 { "manager", "Manager", UL_MANAGER
, '%' },
655 { "coowner", "Coowner", UL_COOWNER
, '*' },
656 { "owner", "Owner", UL_OWNER
, '!' },
657 { "helper", "BUG:", UL_HELPER
, 'X' }
660 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
661 static const struct {
664 unsigned short default_value
;
665 unsigned int old_idx
;
666 unsigned int old_flag
;
667 unsigned short flag_value
;
669 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
670 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
671 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
672 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
673 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
674 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
675 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
676 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
677 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
680 struct charOptionValues
{
683 } automodeValues
[] = {
684 { 'n', "CSMSG_AUTOMODE_NONE" },
685 { 'y', "CSMSG_AUTOMODE_NORMAL" },
686 { 'v', "CSMSG_AUTOMODE_VOICE" },
687 { 'h', "CSMSG_AUTOMODE_HOP" },
688 { 'o', "CSMSG_AUTOMODE_OP" },
689 { 'm', "CSMSG_AUTOMODE_MUTE" }
690 }, protectValues
[] = {
691 { 'a', "CSMSG_PROTECT_ALL" },
692 { 'e', "CSMSG_PROTECT_EQUAL" },
693 { 'l', "CSMSG_PROTECT_LOWER" },
694 { 'n', "CSMSG_PROTECT_NONE" }
696 { 'd', "CSMSG_TOYS_DISABLED" },
697 { 'n', "CSMSG_TOYS_PRIVATE" },
698 { 'p', "CSMSG_TOYS_PUBLIC" }
699 }, topicRefreshValues
[] = {
700 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
701 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
702 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
703 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
704 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
705 }, ctcpReactionValues
[] = {
706 { 'n', "CSMSG_CTCPREACTION_NONE" },
707 { 'k', "CSMSG_CTCPREACTION_KICK" },
708 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
709 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
710 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
711 }, banTimeoutValues
[] = {
712 { '0', "CSMSG_BANTIMEOUT_NONE" },
713 { '1', "CSMSG_BANTIMEOUT_10M" },
714 { '2', "CSMSG_BANTIMEOUT_2H" },
715 { '3', "CSMSG_BANTIMEOUT_4H" },
716 { '4', "CSMSG_BANTIMEOUT_1D" },
717 { '5', "CSMSG_BANTIMEOUT_1W" }
720 { 'n', "CSMSG_RESYNC_NEVER" },
721 { '1', "CSMSG_RESYNC_3_HOURS" },
722 { '2', "CSMSG_RESYNC_6_HOURS" },
723 { '3', "CSMSG_RESYNC_12_HOURS" },
724 { '4', "CSMSG_RESYNC_24_HOURS" }
727 static const struct {
731 unsigned int old_idx
;
733 struct charOptionValues
*values
;
735 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
736 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
737 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
738 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
739 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
740 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
741 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
744 struct userData
*helperList
;
745 struct chanData
*channelList
;
746 static struct module *chanserv_module
;
747 static unsigned int userCount
;
748 unsigned int chanserv_read_version
= 0; /* db version control */
750 #define CHANSERV_DB_VERSION 2
752 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
753 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
756 user_level_from_name(const char *name
, unsigned short clamp_level
)
758 unsigned int level
= 0, ii
;
760 level
= strtoul(name
, NULL
, 10);
761 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
762 if(!irccasecmp(name
, accessLevels
[ii
].name
))
763 level
= accessLevels
[ii
].level
;
764 if(level
> clamp_level
)
770 user_level_name_from_level(int level
)
778 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
779 if(level
>= accessLevels
[ii
].level
)
780 highest
= accessLevels
[ii
].title
;
786 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
789 *minl
= strtoul(arg
, &sep
, 10);
797 *maxl
= strtoul(sep
+1, &sep
, 10);
805 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
807 struct userData
*uData
, **head
;
809 if(!channel
|| !handle
)
812 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
813 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
815 for(uData
= helperList
;
816 uData
&& uData
->handle
!= handle
;
817 uData
= uData
->next
);
821 uData
= calloc(1, sizeof(struct userData
));
822 uData
->handle
= handle
;
824 uData
->access
= UL_HELPER
;
830 uData
->next
= helperList
;
832 helperList
->prev
= uData
;
840 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
841 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
844 head
= &(channel
->users
);
847 if(uData
&& (uData
!= *head
))
849 /* Shuffle the user to the head of whatever list he was in. */
851 uData
->next
->prev
= uData
->prev
;
853 uData
->prev
->next
= uData
->next
;
859 (**head
).prev
= uData
;
866 /* Returns non-zero if user has at least the minimum access.
867 * exempt_owner is set when handling !set, so the owner can set things
870 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
872 struct userData
*uData
;
873 struct chanData
*cData
= channel
->channel_info
;
874 unsigned short minimum
= cData
->lvlOpts
[opt
];
877 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
880 if(minimum
<= uData
->access
)
882 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
887 /* Scan for other users authenticated to the same handle
888 still in the channel. If so, keep them listed as present.
890 user is optional, if not null, it skips checking that userNode
891 (for the handle_part function) */
893 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
897 if(IsSuspended(uData
->channel
)
898 || IsUserSuspended(uData
)
899 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
911 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
913 unsigned int eflags
, argc
;
915 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
917 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
918 if(!channel
->channel_info
919 || IsSuspended(channel
->channel_info
)
921 || !ircncasecmp(text
, "ACTION ", 7))
923 /* We dont punish people we know -Rubin
924 * * Figure out the minimum level needed to CTCP the channel *
926 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
929 /* If they are a user of the channel, they are exempt */
930 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
932 /* We need to enforce against them; do so. */
935 argv
[1] = user
->nick
;
937 if(GetUserMode(channel
, user
))
938 eflags
|= ACTION_KICK
;
939 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
940 default: case 'n': return;
942 eflags
|= ACTION_KICK
;
945 eflags
|= ACTION_BAN
;
948 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
949 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
952 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
953 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
956 argv
[argc
++] = bad_ctcp_reason
;
957 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
961 chanserv_create_note_type(const char *name
)
963 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
964 strcpy(ntype
->name
, name
);
966 dict_insert(note_types
, ntype
->name
, ntype
);
971 chanserv_deref_note_type(void *data
)
973 struct note_type
*ntype
= data
;
975 if(--ntype
->refs
> 0)
981 chanserv_flush_note_type(struct note_type
*ntype
)
983 struct chanData
*cData
;
984 for(cData
= channelList
; cData
; cData
= cData
->next
)
985 dict_remove(cData
->notes
, ntype
->name
);
989 chanserv_truncate_notes(struct note_type
*ntype
)
991 struct chanData
*cData
;
993 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
995 for(cData
= channelList
; cData
; cData
= cData
->next
) {
996 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
999 if(strlen(note
->note
) <= ntype
->max_length
)
1001 dict_remove2(cData
->notes
, ntype
->name
, 1);
1002 note
= realloc(note
, size
);
1003 note
->note
[ntype
->max_length
] = 0;
1004 dict_insert(cData
->notes
, ntype
->name
, note
);
1008 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1010 static struct note
*
1011 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1014 unsigned int len
= strlen(text
);
1016 if(len
> type
->max_length
) len
= type
->max_length
;
1017 note
= calloc(1, sizeof(*note
) + len
);
1019 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1020 memcpy(note
->note
, text
, len
);
1021 note
->note
[len
] = 0;
1022 dict_insert(channel
->notes
, type
->name
, note
);
1028 chanserv_free_note(void *data
)
1030 struct note
*note
= data
;
1032 chanserv_deref_note_type(note
->type
);
1033 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1037 static MODCMD_FUNC(cmd_createnote
) {
1038 struct note_type
*ntype
;
1039 unsigned int arg
= 1, existed
= 0, max_length
;
1041 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1044 ntype
= chanserv_create_note_type(argv
[arg
]);
1045 if(!irccasecmp(argv
[++arg
], "privileged"))
1048 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1049 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1051 else if(!irccasecmp(argv
[arg
], "channel"))
1053 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1056 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1059 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1060 ntype
->set_access
.min_ulevel
= ulvl
;
1062 else if(!irccasecmp(argv
[arg
], "setter"))
1064 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1068 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1072 if(!irccasecmp(argv
[++arg
], "privileged"))
1073 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1074 else if(!irccasecmp(argv
[arg
], "channel_users"))
1075 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1076 else if(!irccasecmp(argv
[arg
], "all"))
1077 ntype
->visible_type
= NOTE_VIS_ALL
;
1079 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1083 if((arg
+1) >= argc
) {
1084 reply("MSG_MISSING_PARAMS", argv
[0]);
1087 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1088 if(max_length
< 20 || max_length
> 450)
1090 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1093 if(existed
&& (max_length
< ntype
->max_length
))
1095 ntype
->max_length
= max_length
;
1096 chanserv_truncate_notes(ntype
);
1098 ntype
->max_length
= max_length
;
1101 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1103 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1108 dict_remove(note_types
, ntype
->name
);
1112 static MODCMD_FUNC(cmd_removenote
) {
1113 struct note_type
*ntype
;
1116 ntype
= dict_find(note_types
, argv
[1], NULL
);
1117 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1120 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1127 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1130 chanserv_flush_note_type(ntype
);
1132 dict_remove(note_types
, argv
[1]);
1133 reply("CSMSG_NOTE_DELETED", argv
[1]);
1138 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1142 if(orig
->modes_set
& change
->modes_clear
)
1144 if(orig
->modes_clear
& change
->modes_set
)
1146 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1147 && strcmp(orig
->new_key
, change
->new_key
))
1149 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1150 && (orig
->new_limit
!= change
->new_limit
))
1155 static char max_length_text
[MAXLEN
+1][16];
1157 static struct helpfile_expansion
1158 chanserv_expand_variable(const char *variable
)
1160 struct helpfile_expansion exp
;
1162 if(!irccasecmp(variable
, "notes"))
1165 exp
.type
= HF_TABLE
;
1166 exp
.value
.table
.length
= 1;
1167 exp
.value
.table
.width
= 3;
1168 exp
.value
.table
.flags
= 0;
1169 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1170 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1171 exp
.value
.table
.contents
[0][0] = "Note Type";
1172 exp
.value
.table
.contents
[0][1] = "Visibility";
1173 exp
.value
.table
.contents
[0][2] = "Max Length";
1174 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1176 struct note_type
*ntype
= iter_data(it
);
1179 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1180 row
= exp
.value
.table
.length
++;
1181 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1182 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1183 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1184 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1186 if(!max_length_text
[ntype
->max_length
][0])
1187 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1188 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1193 exp
.type
= HF_STRING
;
1194 exp
.value
.str
= NULL
;
1198 static struct chanData
*
1199 register_channel(struct chanNode
*cNode
, char *registrar
)
1201 struct chanData
*channel
;
1202 enum levelOption lvlOpt
;
1203 enum charOption chOpt
;
1205 channel
= calloc(1, sizeof(struct chanData
));
1207 channel
->notes
= dict_new();
1208 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1210 channel
->registrar
= strdup(registrar
);
1211 channel
->registered
= now
;
1212 channel
->visited
= now
;
1213 channel
->limitAdjusted
= now
;
1214 channel
->ownerTransfer
= now
;
1215 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1216 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1217 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1218 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1219 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1221 channel
->prev
= NULL
;
1222 channel
->next
= channelList
;
1225 channelList
->prev
= channel
;
1226 channelList
= channel
;
1227 registered_channels
++;
1229 channel
->channel
= cNode
;
1231 cNode
->channel_info
= channel
;
1236 static struct userData
*
1237 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1239 struct userData
*ud
;
1241 if(access
> UL_OWNER
)
1244 ud
= calloc(1, sizeof(*ud
));
1245 ud
->channel
= channel
;
1246 ud
->handle
= handle
;
1248 ud
->access
= access
;
1249 ud
->info
= info
? strdup(info
) : NULL
;
1252 ud
->next
= channel
->users
;
1254 channel
->users
->prev
= ud
;
1255 channel
->users
= ud
;
1257 channel
->userCount
++;
1261 ud
->u_next
= ud
->handle
->channels
;
1263 ud
->u_next
->u_prev
= ud
;
1264 ud
->handle
->channels
= ud
;
1266 ud
->flags
= USER_FLAGS_DEFAULT
;
1270 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1273 del_channel_user(struct userData
*user
, int do_gc
)
1275 struct chanData
*channel
= user
->channel
;
1277 channel
->userCount
--;
1281 user
->prev
->next
= user
->next
;
1283 channel
->users
= user
->next
;
1285 user
->next
->prev
= user
->prev
;
1288 user
->u_prev
->u_next
= user
->u_next
;
1290 user
->handle
->channels
= user
->u_next
;
1292 user
->u_next
->u_prev
= user
->u_prev
;
1296 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1297 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1298 unregister_channel(channel
, "lost all users.");
1302 static struct adduserPending
*
1303 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1305 struct adduserPending
*ap
;
1306 ap
= calloc(1,sizeof(struct adduserPending
));
1307 ap
->channel
= channel
;
1310 ap
->created
= time(NULL
);
1312 /* ap->prev defaults to NULL already.. */
1313 ap
->next
= adduser_pendings
;
1314 if(adduser_pendings
)
1315 adduser_pendings
->prev
= ap
;
1316 adduser_pendings
= ap
;
1317 adduser_pendings_count
++;
1322 del_adduser_pending(struct adduserPending
*ap
)
1325 ap
->prev
->next
= ap
->next
;
1327 adduser_pendings
= ap
->next
;
1330 ap
->next
->prev
= ap
->prev
;
1334 static void expire_adduser_pending();
1336 /* find_adduser_pending(channel, user) will find an arbitrary record
1337 * from user, channel, or user and channel.
1338 * if user or channel are NULL, they will match any records.
1340 static struct adduserPending
*
1341 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1343 struct adduserPending
*ap
;
1345 expire_adduser_pending(); /* why not here.. */
1347 if(!channel
&& !user
) /* 2 nulls matches all */
1348 return(adduser_pendings
);
1349 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1351 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1358 /* Remove all pendings for a user or channel
1360 * called in nickserv.c DelUser() and proto-* unregister_channel()
1363 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1365 struct adduserPending
*ap
;
1367 /* So this is a bit wastefull, i hate dealing with linked lists.
1368 * if its a problem we'll rewrite it right */
1369 while((ap
= find_adduser_pending(channel
, user
))) {
1370 del_adduser_pending(ap
);
1374 /* Called from nickserv.c cmd_auth after someone auths */
1376 process_adduser_pending(struct userNode
*user
)
1378 struct adduserPending
*ap
;
1379 if(!user
->handle_info
)
1380 return; /* not associated with an account */
1381 while((ap
= find_adduser_pending(NULL
, user
)))
1383 struct userData
*actee
;
1384 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1386 /* Already on the userlist. do nothing*/
1390 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1391 scan_user_presence(actee
, NULL
);
1393 del_adduser_pending(ap
);
1398 expire_adduser_pending()
1400 struct adduserPending
*ap
, *ap_next
;
1401 ap
= adduser_pendings
;
1404 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1406 ap_next
= ap
->next
; /* save next */
1407 del_adduser_pending(ap
); /* free and relink */
1408 ap
= ap_next
; /* advance */
1415 static void expire_ban(void *data
);
1418 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1421 unsigned int ii
, l1
, l2
;
1426 bd
= malloc(sizeof(struct banData
));
1428 bd
->channel
= channel
;
1430 bd
->triggered
= triggered
;
1431 bd
->expires
= expires
;
1433 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1435 extern const char *hidden_host_suffix
;
1436 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1440 l2
= strlen(old_name
);
1443 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1445 new_mask
= alloca(MAXLEN
);
1446 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1449 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1451 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1452 bd
->reason
= strdup(reason
);
1455 timeq_add(expires
, expire_ban
, bd
);
1458 bd
->next
= channel
->bans
; /* lamers */
1460 channel
->bans
->prev
= bd
;
1462 channel
->banCount
++;
1469 del_channel_ban(struct banData
*ban
)
1471 ban
->channel
->banCount
--;
1475 ban
->prev
->next
= ban
->next
;
1477 ban
->channel
->bans
= ban
->next
;
1480 ban
->next
->prev
= ban
->prev
;
1483 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1492 expire_ban(void *data
) /* lamer.. */
1494 struct banData
*bd
= data
;
1495 if(!IsSuspended(bd
->channel
))
1497 struct banList bans
;
1498 struct mod_chanmode change
;
1500 bans
= bd
->channel
->channel
->banlist
;
1501 mod_chanmode_init(&change
);
1502 for(ii
=0; ii
<bans
.used
; ii
++)
1504 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1507 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1508 change
.args
[0].u
.hostmask
= bd
->mask
;
1509 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1515 del_channel_ban(bd
);
1518 static void chanserv_expire_suspension(void *data
);
1521 unregister_channel(struct chanData
*channel
, const char *reason
)
1523 struct mod_chanmode change
;
1524 char msgbuf
[MAXLEN
];
1526 /* After channel unregistration, the following must be cleaned
1528 - Channel information.
1530 - Channel bans. (lamers)
1531 - Channel suspension data.
1532 - adduser_pending data.
1533 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1539 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1543 mod_chanmode_init(&change
);
1544 change
.modes_clear
|= MODE_REGISTERED
;
1545 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1548 wipe_adduser_pending(channel
->channel
, NULL
);
1550 while(channel
->users
)
1551 del_channel_user(channel
->users
, 0);
1553 while(channel
->bans
)
1554 del_channel_ban(channel
->bans
);
1556 free(channel
->topic
);
1557 free(channel
->registrar
);
1558 free(channel
->greeting
);
1559 free(channel
->user_greeting
);
1560 free(channel
->topic_mask
);
1563 channel
->prev
->next
= channel
->next
;
1565 channelList
= channel
->next
;
1568 channel
->next
->prev
= channel
->prev
;
1570 if(channel
->suspended
)
1572 struct chanNode
*cNode
= channel
->channel
;
1573 struct suspended
*suspended
, *next_suspended
;
1575 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1577 next_suspended
= suspended
->previous
;
1578 free(suspended
->suspender
);
1579 free(suspended
->reason
);
1580 if(suspended
->expires
)
1581 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1586 cNode
->channel_info
= NULL
;
1588 channel
->channel
->channel_info
= NULL
;
1590 dict_delete(channel
->notes
);
1591 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1592 if(!IsSuspended(channel
))
1593 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1594 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1595 UnlockChannel(channel
->channel
);
1597 registered_channels
--;
1601 expire_channels(UNUSED_ARG(void *data
))
1603 struct chanData
*channel
, *next
;
1604 struct userData
*user
;
1605 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1607 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1608 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1610 for(channel
= channelList
; channel
; channel
= next
)
1612 next
= channel
->next
;
1614 /* See if the channel can be expired. */
1615 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1616 || IsProtected(channel
))
1619 /* Make sure there are no high-ranking users still in the channel. */
1620 for(user
=channel
->users
; user
; user
=user
->next
)
1621 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1626 /* Unregister the channel */
1627 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1628 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1629 unregister_channel(channel
, "registration expired.");
1632 if(chanserv_conf
.channel_expire_frequency
)
1633 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1637 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1639 char protect
= channel
->chOpts
[chProtect
];
1640 struct userData
*cs_victim
, *cs_aggressor
;
1642 /* Don't protect if no one is to be protected, someone is attacking
1643 himself, or if the aggressor is an IRC Operator. */
1644 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1647 /* Don't protect if the victim isn't authenticated (because they
1648 can't be a channel user), unless we are to protect non-users
1650 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1651 if(protect
!= 'a' && !cs_victim
)
1654 /* Protect if the aggressor isn't a user because at this point,
1655 the aggressor can only be less than or equal to the victim. */
1656 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1660 /* If the aggressor was a user, then the victim can't be helped. */
1667 if(cs_victim
->access
> cs_aggressor
->access
)
1672 if(cs_victim
->access
>= cs_aggressor
->access
)
1681 validate_op(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1683 struct chanData
*cData
= channel
->channel_info
;
1684 struct userData
*cs_victim
;
1686 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1687 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1688 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1691 reply("CSMSG_OPBY_LOCKED");
1693 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1701 validate_halfop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1703 struct chanData
*cData
= channel
->channel_info
;
1704 struct userData
*cs_victim
;
1706 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1707 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1708 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1710 reply("CSMSG_HOPBY_LOCKED");
1719 validate_deop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1721 if(IsService(victim
))
1723 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1727 if(protect_user(victim
, user
, channel
->channel_info
))
1729 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1737 validate_dehop(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1739 if(IsService(victim
))
1741 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
1745 if(protect_user(victim
, user
, channel
->channel_info
))
1747 reply("CSMSG_USER_PROTECTED", victim
->nick
);
1754 static struct do_not_register
*
1755 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1757 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1758 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1759 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1760 strcpy(dnr
->reason
, reason
);
1762 if(dnr
->chan_name
[0] == '*')
1763 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1764 else if(strpbrk(dnr
->chan_name
, "*?"))
1765 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1767 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1771 static struct dnrList
1772 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1774 struct dnrList list
;
1776 struct do_not_register
*dnr
;
1778 dnrList_init(&list
);
1779 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1780 dnrList_append(&list
, dnr
);
1781 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1782 dnrList_append(&list
, dnr
);
1784 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1785 if(match_ircglob(chan_name
, iter_key(it
)))
1786 dnrList_append(&list
, iter_data(it
));
1791 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1793 struct dnrList list
;
1794 struct do_not_register
*dnr
;
1796 char buf
[INTERVALLEN
];
1798 list
= chanserv_find_dnrs(chan_name
, handle
);
1799 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1801 dnr
= list
.list
[ii
];
1804 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1805 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1808 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1811 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1816 struct do_not_register
*
1817 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1819 struct do_not_register
*dnr
;
1822 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1826 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1828 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1829 if(match_ircglob(chan_name
, iter_key(it
)))
1830 return iter_data(it
);
1835 static CHANSERV_FUNC(cmd_noregister
)
1838 struct do_not_register
*dnr
;
1839 char buf
[INTERVALLEN
];
1840 unsigned int matches
;
1846 reply("CSMSG_DNR_SEARCH_RESULTS");
1847 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1850 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1852 dnr
= iter_data(it
);
1854 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1856 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1859 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1861 dnr
= iter_data(it
);
1863 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1865 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1868 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1870 dnr
= iter_data(it
);
1872 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1874 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1879 reply("MSG_MATCH_COUNT", matches
);
1881 reply("MSG_NO_MATCHES");
1887 if(!IsChannelName(target
) && (*target
!= '*'))
1889 reply("CSMSG_NOT_DNR", target
);
1895 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1896 if((*target
== '*') && !get_handle_info(target
+ 1))
1898 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1901 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1902 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1906 reply("CSMSG_DNR_SEARCH_RESULTS");
1907 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1910 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1912 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1914 reply("MSG_NO_MATCHES");
1918 static CHANSERV_FUNC(cmd_allowregister
)
1920 const char *chan_name
= argv
[1];
1922 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1924 dict_remove(handle_dnrs
, chan_name
+1);
1925 reply("CSMSG_DNR_REMOVED", chan_name
);
1927 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1929 dict_remove(plain_dnrs
, chan_name
);
1930 reply("CSMSG_DNR_REMOVED", chan_name
);
1932 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1934 dict_remove(mask_dnrs
, chan_name
);
1935 reply("CSMSG_DNR_REMOVED", chan_name
);
1939 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1946 chanserv_get_owned_count(struct handle_info
*hi
)
1948 struct userData
*cList
;
1951 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1952 if(cList
->access
== UL_OWNER
)
1957 static CHANSERV_FUNC(cmd_register
)
1959 struct handle_info
*handle
;
1960 struct chanData
*cData
;
1961 struct modeNode
*mn
;
1962 char reason
[MAXLEN
];
1964 unsigned int new_channel
, force
=0;
1965 struct do_not_register
*dnr
;
1968 if (checkDefCon(DEFCON_NO_NEW_CHANNELS
) && !IsOper(user
)) {
1969 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
1975 if(channel
->channel_info
)
1977 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1981 if(channel
->bad_channel
)
1983 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1987 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1989 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1994 chan_name
= channel
->name
;
2000 reply("MSG_MISSING_PARAMS", cmd
->name
);
2001 svccmd_send_help_brief(user
, chanserv
, cmd
);
2004 if(!IsChannelName(argv
[1]))
2006 reply("MSG_NOT_CHANNEL_NAME");
2010 if(opserv_bad_channel(argv
[1]))
2012 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2017 chan_name
= argv
[1];
2020 if(argc
>= (new_channel
+2))
2022 if(!IsHelping(user
))
2024 reply("CSMSG_PROXY_FORBIDDEN");
2028 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2030 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2031 dnr
= chanserv_is_dnr(chan_name
, handle
);
2033 /* Check if they are over the limit.. */
2034 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2036 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2043 handle
= user
->handle_info
;
2044 dnr
= chanserv_is_dnr(chan_name
, handle
);
2045 /* Check if they are over the limit.. */
2046 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2048 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2051 /* Check if another service is in the channel */
2053 for(n
= 0; n
< channel
->members
.used
; n
++)
2055 mn
= channel
->members
.list
[n
];
2056 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2058 reply("CSMSG_ANOTHER_SERVICE");
2065 if(!IsHelping(user
))
2066 reply("CSMSG_DNR_CHANNEL", chan_name
);
2068 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2072 /* now handled above for message specilization *
2073 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2075 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2081 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2083 cData
= register_channel(channel
, user
->handle_info
->handle
);
2084 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2085 cData
->modes
= chanserv_conf
.default_modes
;
2087 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2088 if (IsOffChannel(cData
))
2090 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2094 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2095 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2096 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2098 mod_chanmode_announce(chanserv
, channel
, change
);
2099 mod_chanmode_free(change
);
2102 /* Initialize the channel's max user record. */
2103 cData
->max
= channel
->members
.used
;
2105 if(handle
!= user
->handle_info
)
2106 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2108 reply("CSMSG_REG_SUCCESS", channel
->name
);
2110 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2111 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2116 make_confirmation_string(struct userData
*uData
)
2118 static char strbuf
[16];
2123 for(src
= uData
->handle
->handle
; *src
; )
2124 accum
= accum
* 31 + toupper(*src
++);
2126 for(src
= uData
->channel
->channel
->name
; *src
; )
2127 accum
= accum
* 31 + toupper(*src
++);
2128 sprintf(strbuf
, "%08x", accum
);
2132 static CHANSERV_FUNC(cmd_unregister
)
2135 char reason
[MAXLEN
];
2136 struct chanData
*cData
;
2137 struct userData
*uData
;
2139 cData
= channel
->channel_info
;
2142 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2146 uData
= GetChannelUser(cData
, user
->handle_info
);
2147 if(!uData
|| (uData
->access
< UL_OWNER
))
2149 reply("CSMSG_NO_ACCESS");
2153 if(IsProtected(cData
))
2155 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2159 if(!IsHelping(user
))
2161 const char *confirm_string
;
2162 if(IsSuspended(cData
))
2164 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2167 confirm_string
= make_confirmation_string(uData
);
2168 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2170 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2175 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2176 name
= strdup(channel
->name
);
2177 unregister_channel(cData
, reason
);
2178 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2179 reply("CSMSG_UNREG_SUCCESS", name
);
2185 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2187 extern struct userNode
*spamserv
;
2188 struct mod_chanmode
*change
;
2190 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2192 change
= mod_chanmode_alloc(2);
2194 change
->args
[0].mode
= MODE_CHANOP
;
2195 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2196 change
->args
[1].mode
= MODE_CHANOP
;
2197 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2201 change
= mod_chanmode_alloc(1);
2203 change
->args
[0].mode
= MODE_CHANOP
;
2204 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2207 mod_chanmode_announce(chanserv
, channel
, change
);
2208 mod_chanmode_free(change
);
2211 static CHANSERV_FUNC(cmd_move
)
2213 struct mod_chanmode change
;
2214 struct chanNode
*target
;
2215 struct modeNode
*mn
;
2216 struct userData
*uData
;
2217 char reason
[MAXLEN
];
2218 struct do_not_register
*dnr
;
2219 int chanserv_join
= 0, spamserv_join
;
2223 if(IsProtected(channel
->channel_info
))
2225 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2229 if(!IsChannelName(argv
[1]))
2231 reply("MSG_NOT_CHANNEL_NAME");
2235 if(opserv_bad_channel(argv
[1]))
2237 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2241 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2243 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2245 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2247 if(!IsHelping(user
))
2248 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2250 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2256 mod_chanmode_init(&change
);
2257 if(!(target
= GetChannel(argv
[1])))
2259 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2260 if(!IsSuspended(channel
->channel_info
))
2263 else if(target
->channel_info
)
2265 reply("CSMSG_ALREADY_REGGED", target
->name
);
2268 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2269 && !IsHelping(user
))
2271 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2274 else if(!IsSuspended(channel
->channel_info
))
2279 /* Clear MODE_REGISTERED from old channel, add it to new. */
2281 change
.modes_clear
= MODE_REGISTERED
;
2282 mod_chanmode_announce(chanserv
, channel
, &change
);
2283 change
.modes_clear
= 0;
2284 change
.modes_set
= MODE_REGISTERED
;
2285 mod_chanmode_announce(chanserv
, target
, &change
);
2288 /* Move the channel_info to the target channel; it
2289 shouldn't be necessary to clear timeq callbacks
2290 for the old channel. */
2291 target
->channel_info
= channel
->channel_info
;
2292 target
->channel_info
->channel
= target
;
2293 channel
->channel_info
= NULL
;
2295 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2298 ss_cs_join_channel(target
, spamserv_join
);
2300 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
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);
2307 UnlockChannel(channel
);
2308 LockChannel(target
);
2309 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2310 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2315 merge_users(struct chanData
*source
, struct chanData
*target
)
2317 struct userData
*suData
, *tuData
, *next
;
2323 /* Insert the source's users into the scratch area. */
2324 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2325 dict_insert(merge
, suData
->handle
->handle
, suData
);
2327 /* Iterate through the target's users, looking for
2328 users common to both channels. The lower access is
2329 removed from either the scratch area or target user
2331 for(tuData
= target
->users
; tuData
; tuData
= next
)
2333 struct userData
*choice
;
2335 next
= tuData
->next
;
2337 /* If a source user exists with the same handle as a target
2338 channel's user, resolve the conflict by removing one. */
2339 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2343 /* Pick the data we want to keep. */
2344 /* If the access is the same, use the later seen time. */
2345 if(suData
->access
== tuData
->access
)
2346 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2347 else /* Otherwise, keep the higher access level. */
2348 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2350 /* Remove the user that wasn't picked. */
2351 if(choice
== tuData
)
2353 dict_remove(merge
, suData
->handle
->handle
);
2354 del_channel_user(suData
, 0);
2357 del_channel_user(tuData
, 0);
2360 /* Move the remaining users to the target channel. */
2361 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2363 suData
= iter_data(it
);
2365 /* Insert the user into the target channel's linked list. */
2366 suData
->prev
= NULL
;
2367 suData
->next
= target
->users
;
2368 suData
->channel
= target
;
2371 target
->users
->prev
= suData
;
2372 target
->users
= suData
;
2374 /* Update the user counts for the target channel; the
2375 source counts are left alone. */
2376 target
->userCount
++;
2379 /* Possible to assert (source->users == NULL) here. */
2380 source
->users
= NULL
;
2385 merge_bans(struct chanData
*source
, struct chanData
*target
)
2387 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2389 /* Hold on to the original head of the target ban list
2390 to avoid comparing source bans with source bans. */
2391 tFront
= target
->bans
;
2393 /* Perform a totally expensive O(n*m) merge, ick. */
2394 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2396 /* Flag to track whether the ban's been moved
2397 to the destination yet. */
2400 /* Possible to assert (sbData->prev == NULL) here. */
2401 sNext
= sbData
->next
;
2403 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2405 tNext
= tbData
->next
;
2407 /* Perform two comparisons between each source
2408 and target ban, conflicts are resolved by
2409 keeping the broader ban and copying the later
2410 expiration and triggered time. */
2411 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2413 /* There is a broader ban in the target channel that
2414 overrides one in the source channel; remove the
2415 source ban and break. */
2416 if(sbData
->expires
> tbData
->expires
)
2417 tbData
->expires
= sbData
->expires
;
2418 if(sbData
->triggered
> tbData
->triggered
)
2419 tbData
->triggered
= sbData
->triggered
;
2420 del_channel_ban(sbData
);
2423 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2425 /* There is a broader ban in the source channel that
2426 overrides one in the target channel; remove the
2427 target ban, fall through and move the source over. */
2428 if(tbData
->expires
> sbData
->expires
)
2429 sbData
->expires
= tbData
->expires
;
2430 if(tbData
->triggered
> sbData
->triggered
)
2431 sbData
->triggered
= tbData
->triggered
;
2432 if(tbData
== tFront
)
2434 del_channel_ban(tbData
);
2437 /* Source bans can override multiple target bans, so
2438 we allow a source to run through this loop multiple
2439 times, but we can only move it once. */
2444 /* Remove the source ban from the source ban list. */
2446 sbData
->next
->prev
= sbData
->prev
;
2448 /* Modify the source ban's associated channel. */
2449 sbData
->channel
= target
;
2451 /* Insert the ban into the target channel's linked list. */
2452 sbData
->prev
= NULL
;
2453 sbData
->next
= target
->bans
;
2456 target
->bans
->prev
= sbData
;
2457 target
->bans
= sbData
;
2459 /* Update the user counts for the target channel. */
2464 /* Possible to assert (source->bans == NULL) here. */
2465 source
->bans
= NULL
;
2469 merge_data(struct chanData
*source
, struct chanData
*target
)
2471 /* Use more recent visited and owner-transfer time; use older
2472 * registered time. Bitwise or may_opchan. Use higher max.
2473 * Do not touch last_refresh, ban count or user counts.
2475 if(source
->visited
> target
->visited
)
2476 target
->visited
= source
->visited
;
2477 if(source
->registered
< target
->registered
)
2478 target
->registered
= source
->registered
;
2479 if(source
->ownerTransfer
> target
->ownerTransfer
)
2480 target
->ownerTransfer
= source
->ownerTransfer
;
2481 if(source
->may_opchan
)
2482 target
->may_opchan
= 1;
2483 if(source
->max
> target
->max
)
2484 target
->max
= source
->max
;
2488 merge_channel(struct chanData
*source
, struct chanData
*target
)
2490 merge_users(source
, target
);
2491 merge_bans(source
, target
);
2492 merge_data(source
, target
);
2495 static CHANSERV_FUNC(cmd_merge
)
2497 struct userData
*target_user
;
2498 struct chanNode
*target
;
2499 char reason
[MAXLEN
];
2503 /* Make sure the target channel exists and is registered to the user
2504 performing the command. */
2505 if(!(target
= GetChannel(argv
[1])))
2507 reply("MSG_INVALID_CHANNEL");
2511 if(!target
->channel_info
)
2513 reply("CSMSG_NOT_REGISTERED", target
->name
);
2517 if(IsProtected(channel
->channel_info
))
2519 reply("CSMSG_MERGE_NODELETE");
2523 if(IsSuspended(target
->channel_info
))
2525 reply("CSMSG_MERGE_SUSPENDED");
2529 if(channel
== target
)
2531 reply("CSMSG_MERGE_SELF");
2535 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2536 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2538 reply("CSMSG_MERGE_NOT_OWNER");
2542 /* Merge the channel structures and associated data. */
2543 merge_channel(channel
->channel_info
, target
->channel_info
);
2544 spamserv_cs_move_merge(user
, channel
, target
, 0);
2545 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2546 unregister_channel(channel
->channel_info
, reason
);
2547 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2551 static CHANSERV_FUNC(cmd_opchan
)
2553 struct mod_chanmode change
;
2554 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2556 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2559 channel
->channel_info
->may_opchan
= 0;
2560 mod_chanmode_init(&change
);
2562 change
.args
[0].mode
= MODE_CHANOP
;
2563 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2564 mod_chanmode_announce(chanserv
, channel
, &change
);
2565 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2569 static CHANSERV_FUNC(cmd_adduser
)
2571 struct userData
*actee
;
2572 struct userData
*actor
;
2573 struct handle_info
*handle
;
2574 unsigned short access
;
2578 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2580 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2584 access
= user_level_from_name(argv
[2], UL_OWNER
);
2587 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2591 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2592 if(actor
->access
<= access
)
2594 reply("CSMSG_NO_BUMP_ACCESS");
2598 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2600 // 'kevin must first authenticate with AuthServ.' is sent to user
2601 struct userNode
*unode
;
2602 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2605 if(find_adduser_pending(channel
, unode
)) {
2606 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2609 if(IsInChannel(channel
, unode
)) {
2610 reply("CSMSG_ADDUSER_PENDING");
2611 add_adduser_pending(channel
, unode
, access
);
2612 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2614 /* this results in user must auth AND not in chan errors. too confusing..
2616 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2624 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2626 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2630 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2631 scan_user_presence(actee
, NULL
);
2632 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2636 static CHANSERV_FUNC(cmd_clvl
)
2638 struct handle_info
*handle
;
2639 struct userData
*victim
;
2640 struct userData
*actor
;
2641 unsigned short new_access
;
2642 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2646 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2648 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2651 if(handle
== user
->handle_info
&& !privileged
)
2653 reply("CSMSG_NO_SELF_CLVL");
2657 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2659 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2663 if(actor
->access
<= victim
->access
&& !privileged
)
2665 reply("MSG_USER_OUTRANKED", handle
->handle
);
2669 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2673 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2677 if(new_access
>= actor
->access
&& !privileged
)
2679 reply("CSMSG_NO_BUMP_ACCESS");
2683 victim
->access
= new_access
;
2684 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2688 static CHANSERV_FUNC(cmd_deluser
)
2690 struct handle_info
*handle
;
2691 struct userData
*victim
;
2692 struct userData
*actor
;
2693 unsigned short access
;
2698 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2700 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2703 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2705 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2711 access
= user_level_from_name(argv
[1], UL_OWNER
);
2714 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2717 if(access
!= victim
->access
)
2719 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2725 access
= victim
->access
;
2728 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2730 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2734 chan_name
= strdup(channel
->name
);
2735 del_channel_user(victim
, 1);
2736 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2742 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2744 struct userData
*actor
, *uData
, *next
;
2746 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2748 if(min_access
> max_access
)
2750 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2754 if((actor
->access
<= max_access
) && !IsHelping(user
))
2756 reply("CSMSG_NO_ACCESS");
2760 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2764 if((uData
->access
>= min_access
)
2765 && (uData
->access
<= max_access
)
2766 && match_ircglob(uData
->handle
->handle
, mask
))
2767 del_channel_user(uData
, 1);
2770 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2774 static CHANSERV_FUNC(cmd_mdelowner
)
2776 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2779 static CHANSERV_FUNC(cmd_mdelcoowner
)
2781 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2784 static CHANSERV_FUNC(cmd_mdelmanager
)
2786 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2789 static CHANSERV_FUNC(cmd_mdelop
)
2791 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2794 static CHANSERV_FUNC(cmd_mdelpeon
)
2796 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2799 static CHANSERV_FUNC(cmd_mdelhalfop
)
2801 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2807 cmd_trim_bans(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2809 struct banData
*bData
, *next
;
2810 char interval
[INTERVALLEN
];
2815 limit
= now
- duration
;
2816 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2820 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2823 del_channel_ban(bData
);
2827 intervalString(interval
, duration
, user
->handle_info
);
2828 reply("CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2833 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
)
2835 struct userData
*actor
, *uData
, *next
;
2836 char interval
[INTERVALLEN
];
2840 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2841 if(min_access
> max_access
)
2843 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2847 if((actor
->access
<= max_access
) && !IsHelping(user
))
2849 reply("CSMSG_NO_ACCESS");
2854 limit
= now
- duration
;
2855 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2859 if((uData
->seen
> limit
)
2861 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2864 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2865 || (!max_access
&& (uData
->access
< actor
->access
)))
2867 del_channel_user(uData
, 1);
2875 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2877 reply("CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2881 static CHANSERV_FUNC(cmd_trim
)
2883 unsigned long duration
;
2884 unsigned short min_level
, max_level
;
2889 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2890 duration
= ParseInterval(argv
[2]);
2893 reply("CSMSG_CANNOT_TRIM");
2897 if(!irccasecmp(argv
[1], "lamers"))
2899 cmd_trim_bans(cmd
, user
, channel
, duration
); /* trim_lamers.. */
2902 else if(!irccasecmp(argv
[1], "users"))
2904 cmd_trim_users(cmd
, user
, channel
, 0, 0, duration
, vacation
);
2907 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2909 cmd_trim_users(cmd
, user
, channel
, min_level
, max_level
, duration
, vacation
);
2912 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2914 cmd_trim_users(cmd
, user
, channel
, min_level
, min_level
, duration
, vacation
);
2919 reply("CSMSG_INVALID_TRIM", argv
[1]);
2924 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2925 to the user. cmd_all takes advantage of this. */
2926 static CHANSERV_FUNC(cmd_up
)
2928 struct mod_chanmode change
;
2929 struct userData
*uData
;
2932 mod_chanmode_init(&change
);
2934 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2935 if(!change
.args
[0].u
.member
)
2938 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2942 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2946 reply("CSMSG_GODMODE_UP", argv
[0]);
2949 else if(uData
->access
>= UL_OP
)
2951 change
.args
[0].mode
= MODE_CHANOP
;
2952 errmsg
= "CSMSG_ALREADY_OPPED";
2954 else if(uData
->access
>= UL_HALFOP
)
2956 change
.args
[0].mode
= MODE_HALFOP
;
2957 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2959 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2961 change
.args
[0].mode
= MODE_VOICE
;
2962 errmsg
= "CSMSG_ALREADY_VOICED";
2967 reply("CSMSG_NO_ACCESS");
2970 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2971 if(!change
.args
[0].mode
)
2974 reply(errmsg
, channel
->name
);
2977 modcmd_chanmode_announce(&change
);
2981 static CHANSERV_FUNC(cmd_down
)
2983 struct mod_chanmode change
;
2985 mod_chanmode_init(&change
);
2987 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2988 if(!change
.args
[0].u
.member
)
2991 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2995 if(!change
.args
[0].u
.member
->modes
)
2998 reply("CSMSG_ALREADY_DOWN", channel
->name
);
3002 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
3003 modcmd_chanmode_announce(&change
);
3007 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
)
3009 struct userData
*cList
;
3011 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3013 if(IsSuspended(cList
->channel
)
3014 || IsUserSuspended(cList
)
3015 || !GetUserMode(cList
->channel
->channel
, user
))
3018 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3024 static CHANSERV_FUNC(cmd_upall
)
3026 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3029 static CHANSERV_FUNC(cmd_downall
)
3031 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3034 typedef int validate_func_t(struct svccmd
*cmd
, struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3035 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3038 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
)
3040 unsigned int ii
, valid
;
3041 struct userNode
*victim
;
3042 struct mod_chanmode
*change
;
3044 change
= mod_chanmode_alloc(argc
- 1);
3046 for(ii
=valid
=0; ++ii
< argc
; )
3048 if(!(victim
= GetUserH(argv
[ii
])))
3050 change
->args
[valid
].mode
= mode
;
3051 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3052 if(!change
->args
[valid
].u
.member
)
3054 if(validate
&& !validate(cmd
, user
, channel
, victim
))
3059 change
->argc
= valid
;
3060 if(valid
< (argc
-1))
3061 reply("CSMSG_PROCESS_FAILED");
3064 modcmd_chanmode_announce(change
);
3065 reply(action
, channel
->name
);
3067 mod_chanmode_free(change
);
3071 static CHANSERV_FUNC(cmd_op
)
3073 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3076 static CHANSERV_FUNC(cmd_hop
)
3078 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3081 static CHANSERV_FUNC(cmd_deop
)
3083 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3086 static CHANSERV_FUNC(cmd_dehop
)
3088 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3091 static CHANSERV_FUNC(cmd_voice
)
3093 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3096 static CHANSERV_FUNC(cmd_devoice
)
3098 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3102 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3108 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3110 struct modeNode
*mn
= channel
->members
.list
[ii
];
3112 if(IsService(mn
->user
))
3115 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3118 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3122 victims
[(*victimCount
)++] = mn
;
3128 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3130 struct userNode
*victim
;
3131 struct modeNode
**victims
;
3132 unsigned int offset
, n
, victimCount
, duration
= 0;
3133 char *reason
= "Bye.", *ban
, *name
;
3134 char interval
[INTERVALLEN
];
3136 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3137 REQUIRE_PARAMS(offset
);
3140 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3141 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3143 /* Truncate the reason to a length of TOPICLEN, as
3144 the ircd does; however, leave room for an ellipsis
3145 and the kicker's nick. */
3146 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3150 if((victim
= GetUserH(argv
[1])))
3152 victims
= alloca(sizeof(victims
[0]));
3153 victims
[0] = GetUserMode(channel
, victim
);
3154 /* XXX: The comparison with ACTION_KICK is just because all
3155 * other actions can work on users outside the channel, and we
3156 * want to allow those (e.g. unbans) in that case. If we add
3157 * some other ejection action for in-channel users, change
3159 victimCount
= victims
[0] ? 1 : 0;
3161 if(IsService(victim
))
3164 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3168 if((action
== ACTION_KICK
) && !victimCount
)
3171 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3175 if(protect_user(victim
, user
, channel
->channel_info
))
3177 // This translates to send_message(user, cmd->parent->bot, ...)
3178 // if user is x3 (ctcp action) cmd is null and segfault.
3180 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3184 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3185 name
= victim
->nick
;
3189 if(!is_ircmask(argv
[1]))
3192 reply("MSG_NICK_UNKNOWN", argv
[1]);
3196 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3198 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3201 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3204 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3205 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3207 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3208 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3209 some creativity, but its not x3's job to be the ban censor anyway. */
3210 if(is_overmask(argv
[1]))
3213 reply("CSMSG_LAME_MASK", argv
[1]);
3217 if((action
== ACTION_KICK
) && (victimCount
== 0))
3220 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3224 name
= ban
= strdup(argv
[1]);
3227 /* Truncate the ban in place if necessary; we must ensure
3228 that 'ban' is a valid ban mask before sanitizing it. */
3229 sanitize_ircmask(ban
);
3231 if(action
& ACTION_ADD_LAMER
)
3233 struct banData
*bData
, *next
;
3235 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3238 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3243 if(action
& ACTION_ADD_TIMED_LAMER
)
3245 duration
= ParseInterval(argv
[2]);
3250 reply("CSMSG_DURATION_TOO_LOW");
3254 else if(duration
> (86400 * 365 * 2))
3257 reply("CSMSG_DURATION_TOO_HIGH");
3264 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3266 if(match_ircglobs(bData
->mask
, ban
))
3268 int exact
= !irccasecmp(bData
->mask
, ban
);
3270 /* The ban is redundant; there is already a ban
3271 with the same effect in place. */
3275 free(bData
->reason
);
3276 bData
->reason
= strdup(reason
);
3277 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3279 reply("CSMSG_REASON_CHANGE", ban
);
3283 if(exact
&& bData
->expires
)
3287 /* If the ban matches an existing one exactly,
3288 extend the expiration time if the provided
3289 duration is longer. */
3290 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3292 bData
->expires
= now
+ duration
;
3303 /* Delete the expiration timeq entry and
3304 requeue if necessary. */
3305 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3308 timeq_add(bData
->expires
, expire_ban
, bData
);
3312 /* automated kickban, dont reply */
3315 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3317 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3323 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3330 if(match_ircglobs(ban
, bData
->mask
))
3332 /* The ban we are adding makes previously existing
3333 bans redundant; silently remove them. */
3334 del_channel_ban(bData
);
3338 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
);
3340 name
= ban
= strdup(bData
->mask
);
3344 /* WHAT DOES THIS DO?? -Rubin */
3345 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3347 extern const char *hidden_host_suffix
;
3348 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3350 unsigned int l1
, l2
;
3353 l2
= strlen(old_name
);
3356 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3358 new_mask
= malloc(MAXLEN
);
3359 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3361 name
= ban
= new_mask
;
3366 if(action
& ACTION_BAN
)
3368 unsigned int exists
;
3369 struct mod_chanmode
*change
;
3371 if(channel
->banlist
.used
>= MAXBANS
)
3374 reply("CSMSG_BANLIST_FULL", channel
->name
);
3379 exists
= ChannelBanExists(channel
, ban
);
3380 change
= mod_chanmode_alloc(victimCount
+ 1);
3381 for(n
= 0; n
< victimCount
; ++n
)
3383 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3384 change
->args
[n
].u
.member
= victims
[n
];
3388 change
->args
[n
].mode
= MODE_BAN
;
3389 change
->args
[n
++].u
.hostmask
= ban
;
3393 modcmd_chanmode_announce(change
);
3395 mod_chanmode_announce(chanserv
, channel
, change
);
3396 mod_chanmode_free(change
);
3398 if(exists
&& (action
== ACTION_BAN
))
3401 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3407 if(action
& ACTION_KICK
)
3409 char kick_reason
[MAXLEN
];
3410 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3412 for(n
= 0; n
< victimCount
; n
++)
3413 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3418 /* No response, since it was automated. */
3420 else if(action
& ACTION_ADD_LAMER
)
3423 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3425 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3427 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3428 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3429 else if(action
& ACTION_BAN
)
3430 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3431 else if(action
& ACTION_KICK
&& victimCount
)
3432 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3438 static CHANSERV_FUNC(cmd_kickban
)
3440 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3443 static CHANSERV_FUNC(cmd_kick
)
3445 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3448 static CHANSERV_FUNC(cmd_ban
)
3450 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3453 static CHANSERV_FUNC(cmd_addlamer
)
3455 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3458 static CHANSERV_FUNC(cmd_addtimedlamer
)
3460 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3463 static struct mod_chanmode
*
3464 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3466 struct mod_chanmode
*change
;
3467 unsigned char *match
;
3468 unsigned int ii
, count
;
3470 match
= alloca(bans
->used
);
3473 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3475 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3476 MATCH_USENICK
| MATCH_VISIBLE
);
3483 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3485 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3492 change
= mod_chanmode_alloc(count
);
3493 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3497 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3498 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3500 assert(count
== change
->argc
);
3504 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3506 unsigned int jj
, ii
, count
;
3508 struct chanData
*channel
;
3510 struct mod_chanmode
*change
;
3512 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3513 /* Walk through every channel */
3514 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3515 switch(channel
->chOpts
[chBanTimeout
])
3517 default: case '0': continue; /* Dont remove bans in this chan */
3518 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3519 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3520 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3521 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3522 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3525 /* First find out how many bans were going to unset */
3526 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3527 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3531 /* At least one ban, so setup a removal */
3532 change
= mod_chanmode_alloc(count
);
3534 /* Walk over every ban in this channel.. */
3535 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3536 bn
= channel
->channel
->banlist
.list
[jj
];
3537 if (bn
->set
< bantimeout
) {
3538 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3540 /* Add this ban to the mode change */
3541 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3542 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3544 /* Pull this ban out of the list */
3545 banList_remove(&(channel
->channel
->banlist
), bn
);
3550 /* Send the modes to IRC */
3551 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3553 /* free memory from strdup above */
3554 for(ii
= 0; ii
< count
; ++ii
)
3555 free((char*)change
->args
[ii
].u
.hostmask
);
3557 mod_chanmode_free(change
);
3560 /* Set this function to run again */
3561 if(chanserv_conf
.ban_timeout_frequency
)
3562 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3567 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3569 struct userNode
*actee
;
3575 /* may want to allow a comma delimited list of users... */
3576 if(!(actee
= GetUserH(argv
[1])))
3578 if(!is_ircmask(argv
[1]))
3580 reply("MSG_NICK_UNKNOWN", argv
[1]);
3584 mask
= strdup(argv
[1]);
3587 /* We don't sanitize the mask here because ircu
3589 if(action
& ACTION_UNBAN
)
3591 struct mod_chanmode
*change
;
3592 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3597 modcmd_chanmode_announce(change
);
3598 for(ii
= 0; ii
< change
->argc
; ++ii
)
3599 free((char*)change
->args
[ii
].u
.hostmask
);
3600 mod_chanmode_free(change
);
3605 if(action
& ACTION_DEL_LAMER
)
3607 struct banData
*ban
, *next
;
3609 ban
= channel
->channel_info
->bans
; /* lamers */
3613 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3616 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3621 del_channel_ban(ban
);
3628 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3630 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3636 static CHANSERV_FUNC(cmd_unban
)
3638 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3641 static CHANSERV_FUNC(cmd_dellamer
)
3643 /* it doesn't necessarily have to remove the channel ban - may want
3644 to make that an option. */
3645 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3648 static CHANSERV_FUNC(cmd_unbanme
)
3650 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3651 long flags
= ACTION_UNBAN
;
3653 /* remove permanent bans if the user has the proper access. */
3654 if(uData
->access
>= UL_MANAGER
)
3655 flags
|= ACTION_DEL_LAMER
;
3657 argv
[1] = user
->nick
;
3658 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3661 static CHANSERV_FUNC(cmd_unbanall
)
3663 struct mod_chanmode
*change
;
3666 if(!channel
->banlist
.used
)
3668 reply("CSMSG_NO_BANS", channel
->name
);
3672 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3673 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3675 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3676 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3678 modcmd_chanmode_announce(change
);
3679 for(ii
= 0; ii
< change
->argc
; ++ii
)
3680 free((char*)change
->args
[ii
].u
.hostmask
);
3681 mod_chanmode_free(change
);
3682 reply("CSMSG_BANS_REMOVED", channel
->name
);
3686 static CHANSERV_FUNC(cmd_open
)
3688 struct mod_chanmode
*change
;
3691 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3693 change
= mod_chanmode_alloc(0);
3694 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3695 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3696 && channel
->channel_info
->modes
.modes_set
)
3697 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3698 modcmd_chanmode_announce(change
);
3699 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3700 for(ii
= 0; ii
< change
->argc
; ++ii
)
3701 free((char*)change
->args
[ii
].u
.hostmask
);
3702 mod_chanmode_free(change
);
3706 static CHANSERV_FUNC(cmd_myaccess
)
3708 static struct string_buffer sbuf
;
3709 struct handle_info
*target_handle
;
3710 struct userData
*uData
;
3713 target_handle
= user
->handle_info
;
3714 else if(!IsHelping(user
))
3716 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3719 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3722 if(!target_handle
->channels
)
3724 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3728 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3729 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3731 struct chanData
*cData
= uData
->channel
;
3733 if(uData
->access
> UL_OWNER
)
3735 if(IsProtected(cData
)
3736 && (target_handle
!= user
->handle_info
)
3737 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3740 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3741 if(uData
->flags
== USER_AUTO_OP
)
3742 string_buffer_append(&sbuf
, ',');
3743 if(IsUserSuspended(uData
))
3744 string_buffer_append(&sbuf
, 's');
3745 if(IsUserAutoOp(uData
))
3747 if(uData
->access
>= UL_OP
)
3748 string_buffer_append(&sbuf
, 'o');
3749 else if(uData
->access
>= UL_HALFOP
)
3750 string_buffer_append(&sbuf
, 'h');
3751 else if(uData
->access
>= UL_PEON
)
3752 string_buffer_append(&sbuf
, 'v');
3754 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3755 string_buffer_append(&sbuf
, 'i');
3757 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3759 string_buffer_append_string(&sbuf
, ")]");
3760 string_buffer_append(&sbuf
, '\0');
3761 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3767 static CHANSERV_FUNC(cmd_access
)
3769 struct userNode
*target
;
3770 struct handle_info
*target_handle
;
3771 struct userData
*uData
;
3773 char prefix
[MAXLEN
];
3778 target_handle
= target
->handle_info
;
3780 else if((target
= GetUserH(argv
[1])))
3782 target_handle
= target
->handle_info
;
3784 else if(argv
[1][0] == '*')
3786 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3788 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3794 reply("MSG_NICK_UNKNOWN", argv
[1]);
3798 assert(target
|| target_handle
);
3800 if(target
== chanserv
)
3802 reply("CSMSG_IS_CHANSERV");
3810 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3815 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3818 reply("MSG_AUTHENTICATE");
3824 const char *epithet
= NULL
, *type
= NULL
;
3827 epithet
= chanserv_conf
.irc_operator_epithet
;
3830 else if(IsNetworkHelper(target
))
3832 epithet
= chanserv_conf
.network_helper_epithet
;
3833 type
= "network helper";
3835 else if(IsSupportHelper(target
))
3837 epithet
= chanserv_conf
.support_helper_epithet
;
3838 type
= "support helper";
3842 if(target_handle
->epithet
)
3843 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3845 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3847 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3851 sprintf(prefix
, "%s", target_handle
->handle
);
3854 if(!channel
->channel_info
)
3856 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3860 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3861 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3862 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3864 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3865 /* To prevent possible information leaks, only show infolines
3866 * if the requestor is in the channel or it's their own
3868 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3870 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3872 /* Likewise, only say it's suspended if the user has active
3873 * access in that channel or it's their own entry. */
3874 if(IsUserSuspended(uData
)
3875 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3876 || (user
->handle_info
== uData
->handle
)))
3878 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3883 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3889 /* This is never used...
3891 zoot_list(struct listData *list)
3893 struct userData *uData;
3894 unsigned int start, curr, highest, lowest;
3895 struct helpfile_table tmp_table;
3896 const char **temp, *msg;
3898 if(list->table.length == 1)
3901 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);
3903 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));
3904 msg = user_find_message(list->user, "MSG_NONE");
3905 send_message_type(4, list->user, list->bot, " %s", msg);
3907 tmp_table.width = list->table.width;
3908 tmp_table.flags = list->table.flags;
3909 list->table.contents[0][0] = " ";
3910 highest = list->highest;
3911 if(list->lowest != 0)
3912 lowest = list->lowest;
3913 else if(highest < 100)
3916 lowest = highest - 100;
3917 for(start = curr = 1; curr < list->table.length; )
3919 uData = list->users[curr-1];
3920 list->table.contents[curr++][0] = " ";
3921 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3924 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);
3926 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));
3927 temp = list->table.contents[--start];
3928 list->table.contents[start] = list->table.contents[0];
3929 tmp_table.contents = list->table.contents + start;
3930 tmp_table.length = curr - start;
3931 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3932 list->table.contents[start] = temp;
3934 highest = lowest - 1;
3935 lowest = (highest < 100) ? 0 : (highest - 99);
3942 normal_list(struct listData
*list
)
3946 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
);
3948 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
));
3949 if(list
->table
.length
== 1)
3951 msg
= user_find_message(list
->user
, "MSG_NONE");
3952 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3955 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3958 /* if these need changed, uncomment and customize
3960 clean_list(struct listData *list)
3964 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);
3966 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));
3967 if(list->table.length == 1)
3969 msg = user_find_message(list->user, "MSG_NONE");
3970 send_message_type(4, list->user, list->bot, " %s", msg);
3973 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3977 advanced_list(struct listData *list)
3981 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);
3983 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));
3984 if(list->table.length == 1)
3986 msg = user_find_message(list->user, "MSG_NONE");
3987 send_message_type(4, list->user, list->bot, " %s", msg);
3990 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3994 classic_list(struct listData *list)
3998 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4000 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4001 if(list->table.length == 1)
4003 msg = user_find_message(list->user, "MSG_NONE");
4004 send_message_type(4, list->user, list->bot, " %s", msg);
4007 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4012 userData_access_comp(const void *arg_a
, const void *arg_b
)
4014 const struct userData
*a
= *(struct userData
**)arg_a
;
4015 const struct userData
*b
= *(struct userData
**)arg_b
;
4017 if(a
->access
!= b
->access
)
4018 res
= b
->access
- a
->access
;
4020 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4025 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4027 void (*send_list
)(struct listData
*);
4028 struct userData
*uData
;
4029 struct listData lData
;
4030 unsigned int matches
;
4036 lData
.bot
= cmd
->parent
->bot
;
4037 lData
.channel
= channel
;
4038 lData
.lowest
= lowest
;
4039 lData
.highest
= highest
;
4040 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4041 send_list
= normal_list
;
4042 /* What does the following line do exactly?? */
4043 /*(void)zoot_list; ** since it doesn't show user levels */
4046 if(user->handle_info)
4048 switch(user->handle_info->userlist_style)
4050 case HI_STYLE_CLEAN:
4051 send_list = clean_list;
4053 case HI_STYLE_ADVANCED:
4054 send_list = advanced_list;
4056 case HI_STYLE_CLASSIC:
4057 send_list = classic_list;
4059 case HI_STYLE_NORMAL:
4061 send_list = normal_list;
4066 send_list
= normal_list
;
4068 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4070 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4072 if((uData
->access
< lowest
)
4073 || (uData
->access
> highest
)
4074 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4076 lData
.users
[matches
++] = uData
;
4078 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4080 lData
.table
.length
= matches
+1;
4081 lData
.table
.flags
= TABLE_NO_FREE
;
4082 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4084 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4085 lData
.table
.width
= 5; /* with level = 5 */
4087 lData
.table
.width
= 4; /* without = 4 */
4088 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4089 lData
.table
.contents
[0] = ary
;
4090 if(user
->handle_info
) {
4091 switch(user
->handle_info
->userlist_style
) {
4092 case HI_STYLE_CLASSIC
:
4095 case HI_STYLE_ADVANCED
:
4096 ary
[i
++] = "Access";
4099 case HI_STYLE_CLEAN
:
4100 ary
[i
++] = "Access";
4102 case HI_STYLE_NORMAL
:
4104 ary
[i
++] = "Access";
4109 ary
[i
++] = "Access";
4111 ary
[i
++] = "Account";
4112 ary
[i
] = "Last Seen";
4114 ary
[i
++] = "Status";
4115 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4117 struct userData
*uData
= lData
.users
[matches
-1];
4118 char seen
[INTERVALLEN
];
4121 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4122 lData
.table
.contents
[matches
] = ary
;
4123 if(user
->handle_info
) {
4124 switch(user
->handle_info
->userlist_style
) {
4125 case HI_STYLE_CLASSIC
:
4126 ary
[i
++] = strtab(uData
->access
);
4128 case HI_STYLE_ADVANCED
:
4129 ary
[i
++] = user_level_name_from_level(uData
->access
);
4130 ary
[i
++] = strtab(uData
->access
);
4132 case HI_STYLE_CLEAN
:
4133 ary
[i
++] = user_level_name_from_level(uData
->access
);
4135 case HI_STYLE_NORMAL
:
4137 ary
[i
++] = user_level_name_from_level(uData
->access
);
4142 ary
[i
++] = user_level_name_from_level(uData
->access
);
4144 ary
[i
++] = uData
->handle
->handle
;
4147 else if(!uData
->seen
)
4150 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4151 ary
[i
] = strdup(ary
[i
]);
4153 if(IsUserSuspended(uData
))
4154 ary
[i
++] = "Suspended";
4155 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4156 ary
[i
++] = "Vacation";
4158 ary
[i
++] = "Normal";
4161 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4163 /* Free strdup above */
4164 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4165 free(lData
.table
.contents
[matches
]);
4167 free(lData
.table
.contents
[0]);
4168 free(lData
.table
.contents
);
4172 /* Remove this now that debugging is over? or improve it for
4173 * users? Would it be better tied into USERS somehow? -Rubin */
4174 static CHANSERV_FUNC(cmd_pending
)
4176 struct adduserPending
*ap
;
4177 reply("CSMSG_ADDUSER_PENDING_HEADER");
4178 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4180 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4181 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4182 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4186 static CHANSERV_FUNC(cmd_users
)
4188 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4191 static CHANSERV_FUNC(cmd_wlist
)
4193 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4196 static CHANSERV_FUNC(cmd_clist
)
4198 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4201 static CHANSERV_FUNC(cmd_mlist
)
4203 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4206 static CHANSERV_FUNC(cmd_olist
)
4208 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4211 static CHANSERV_FUNC(cmd_hlist
)
4213 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4216 static CHANSERV_FUNC(cmd_plist
)
4218 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4221 static CHANSERV_FUNC(cmd_lamers
)
4223 struct helpfile_table tbl
;
4224 unsigned int matches
= 0, timed
= 0, ii
;
4225 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4226 const char *msg_never
, *triggered
, *expires
;
4227 struct banData
*ban
, **bans
; /* lamers */
4234 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4235 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4238 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4240 if(search
&& !match_ircglobs(search
, ban
->mask
))
4242 bans
[matches
++] = ban
;
4247 tbl
.length
= matches
+ 1;
4248 tbl
.width
= 4 + timed
;
4250 tbl
.flags
= TABLE_NO_FREE
;
4251 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4252 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4253 tbl
.contents
[0][0] = "Mask";
4254 tbl
.contents
[0][1] = "Set By";
4255 tbl
.contents
[0][2] = "Triggered";
4258 tbl
.contents
[0][3] = "Expires";
4259 tbl
.contents
[0][4] = "Reason";
4262 tbl
.contents
[0][3] = "Reason";
4265 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4267 free(tbl
.contents
[0]);
4272 msg_never
= user_find_message(user
, "MSG_NEVER");
4273 for(ii
= 0; ii
< matches
; )
4279 else if(ban
->expires
)
4280 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4282 expires
= msg_never
;
4285 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4287 triggered
= msg_never
;
4289 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4290 tbl
.contents
[ii
][0] = ban
->mask
;
4291 tbl
.contents
[ii
][1] = ban
->owner
;
4292 tbl
.contents
[ii
][2] = strdup(triggered
);
4295 tbl
.contents
[ii
][3] = strdup(expires
);
4296 tbl
.contents
[ii
][4] = ban
->reason
;
4299 tbl
.contents
[ii
][3] = ban
->reason
;
4301 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4302 /* reply("MSG_MATCH_COUNT", matches); */
4303 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4305 free((char*)tbl
.contents
[ii
][2]);
4307 free((char*)tbl
.contents
[ii
][3]);
4308 free(tbl
.contents
[ii
]);
4310 free(tbl
.contents
[0]);
4317 * return + if the user does NOT have the right to set the topic, and
4318 * the topic is changed.
4321 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4323 struct chanData
*cData
= channel
->channel_info
;
4324 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4326 else if(cData
->topic
)
4327 return irccasecmp(new_topic
, cData
->topic
);
4334 * Makes a givin topic fit into a givin topic mask and returns
4337 * topic_mask - the mask to conform to
4338 * topic - the topic to make conform
4339 * new_topic - the pre-allocated char* to put the new topic into
4341 * modifies: new_topic
4344 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4346 //char *topic_mask = cData->topic_mask;
4348 int pos
=0, starpos
=-1, dpos
=0, len
;
4350 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4357 strcpy(new_topic
, "");
4360 len
= strlen(topic
);
4361 if((dpos
+ len
) > TOPICLEN
)
4362 len
= TOPICLEN
+ 1 - dpos
;
4363 memcpy(new_topic
+dpos
, topic
, len
);
4367 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4368 default: new_topic
[dpos
++] = tchar
; break;
4371 if((dpos
> TOPICLEN
) || tchar
)
4373 strcpy(new_topic
, "");
4376 new_topic
[dpos
] = 0;
4380 static CHANSERV_FUNC(cmd_topic
)
4382 struct chanData
*cData
;
4386 #ifdef WITH_PROTOCOL_P10
4390 cData
= channel
->channel_info
;
4395 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4396 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4397 reply("CSMSG_TOPIC_SET", cData
->topic
);
4401 reply("CSMSG_NO_TOPIC", channel
->name
);
4405 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4406 /* If they say "!topic *", use an empty topic. */
4407 if((topic
[0] == '*') && (topic
[1] == 0))
4410 if(bad_topic(channel
, user
, topic
))
4412 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4417 /* If there is a topicmask set, and the new topic doesnt match, make it */
4418 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4420 char *topic_mask
= cData
->topic_mask
;
4421 char new_topic
[TOPICLEN
+1];
4423 /* make a new topic fitting mask */
4424 conform_topic(topic_mask
, topic
, new_topic
);
4427 /* Topic couldnt fit into mask, was too long */
4428 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4429 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4432 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4434 else /* No mask set, just set the topic */
4435 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4438 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4440 /* Grab the topic and save it as the default topic. */
4442 cData
->topic
= strdup(channel
->topic
);
4448 static CHANSERV_FUNC(cmd_mode
)
4450 struct userData
*uData
;
4451 struct mod_chanmode
*change
;
4456 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
4457 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4461 change
= &channel
->channel_info
->modes
;
4462 if(change
->modes_set
|| change
->modes_clear
) {
4463 modcmd_chanmode_announce(change
);
4464 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4466 reply("CSMSG_NO_MODES", channel
->name
);
4470 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4472 base_oplevel
= MAXOPLEVEL
;
4473 else if (uData
->access
>= UL_OWNER
)
4476 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4477 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4481 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4485 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4486 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4489 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4490 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4494 modcmd_chanmode_announce(change
);
4495 mod_chanmode_free(change
);
4496 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4500 static CHANSERV_FUNC(cmd_invite
)
4502 struct userData
*uData
;
4503 struct userNode
*invite
;
4505 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4509 if(!(invite
= GetUserH(argv
[1])))
4511 reply("MSG_NICK_UNKNOWN", argv
[1]);
4518 if(GetUserMode(channel
, invite
))
4520 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4528 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4529 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4532 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4535 if (invite
->handle_info
&& invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4537 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4538 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4539 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4545 irc_invite(chanserv
, invite
, channel
);
4547 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4552 static CHANSERV_FUNC(cmd_inviteme
)
4554 if(GetUserMode(channel
, user
))
4556 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4559 if(channel
->channel_info
4560 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4562 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4565 irc_invite(cmd
->parent
->bot
, user
, channel
);
4570 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4573 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4575 /* We display things based on two dimensions:
4576 * - Issue time: present or absent
4577 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4578 * (in order of precedence, so something both expired and revoked
4579 * only counts as revoked)
4581 combo
= (suspended
->issued
? 4 : 0)
4582 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4584 case 0: /* no issue time, indefinite expiration */
4585 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4587 case 1: /* no issue time, expires in future */
4588 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4589 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4591 case 2: /* no issue time, expired */
4592 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4593 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4595 case 3: /* no issue time, revoked */
4596 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4597 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4599 case 4: /* issue time set, indefinite expiration */
4600 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4601 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4603 case 5: /* issue time set, expires in future */
4604 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4605 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4606 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4608 case 6: /* issue time set, expired */
4609 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4610 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4611 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4613 case 7: /* issue time set, revoked */
4614 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4615 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4616 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4619 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4625 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4628 const char *fmt
= "%a %b %d %H:%M %Y";
4629 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4631 if(giveownership
->staff_issuer
)
4633 if(giveownership
->reason
)
4634 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4635 giveownership
->target
, giveownership
->target_access
,
4636 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4638 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4639 giveownership
->target
, giveownership
->target_access
,
4640 giveownership
->staff_issuer
, buf
);
4644 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4649 static CHANSERV_FUNC(cmd_info
)
4651 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4652 struct userData
*uData
, *owner
;
4653 struct chanData
*cData
;
4654 struct do_not_register
*dnr
;
4659 cData
= channel
->channel_info
;
4660 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4661 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4664 uData
= GetChannelUser(cData
, user
->handle_info
);
4665 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4667 mod_chanmode_format(&cData
->modes
, modes
);
4668 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4669 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4672 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4676 note
= iter_data(it
);
4677 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4680 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4681 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4684 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4685 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4686 if(owner
->access
== UL_OWNER
)
4687 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4688 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4689 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4690 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4692 privileged
= IsStaff(user
);
4693 /* if(privileged) */
4694 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4695 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData
->registrar
)
4696 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4698 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4699 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4701 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4703 struct suspended
*suspended
;
4704 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4705 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4706 show_suspension_info(cmd
, user
, suspended
);
4708 else if(IsSuspended(cData
))
4710 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4711 show_suspension_info(cmd
, user
, cData
->suspended
);
4713 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4715 struct giveownership
*giveownership
;
4716 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4717 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4718 show_giveownership_info(cmd
, user
, giveownership
);
4720 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4721 reply("CSMSG_CHANNEL_END");
4723 reply("CSMSG_CHANNEL_END_CLEAN");
4727 static CHANSERV_FUNC(cmd_netinfo
)
4729 extern time_t boot_time
;
4730 extern unsigned long burst_length
;
4731 char interval
[INTERVALLEN
];
4733 reply("CSMSG_NETWORK_INFO");
4734 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4735 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4736 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4737 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4738 reply("CSMSG_NETWORK_LAMERS", banCount
);
4739 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4740 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4741 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4746 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4748 struct helpfile_table table
;
4750 struct userNode
*user
;
4755 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4756 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4757 for(nn
=0; nn
<list
->used
; nn
++)
4759 user
= list
->list
[nn
];
4760 if(user
->modes
& skip_flags
)
4764 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4767 nick
= alloca(strlen(user
->nick
)+3);
4768 sprintf(nick
, "(%s)", user
->nick
);
4772 table
.contents
[table
.length
][0] = nick
;
4775 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4778 static CHANSERV_FUNC(cmd_ircops
)
4780 reply("CSMSG_STAFF_OPERS");
4781 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4785 static CHANSERV_FUNC(cmd_helpers
)
4787 reply("CSMSG_STAFF_HELPERS");
4788 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4792 static CHANSERV_FUNC(cmd_staff
)
4794 reply("CSMSG_NETWORK_STAFF");
4795 cmd_ircops(CSFUNC_ARGS
);
4796 cmd_helpers(CSFUNC_ARGS
);
4800 static CHANSERV_FUNC(cmd_peek
)
4802 struct modeNode
*mn
;
4803 char modes
[MODELEN
];
4805 struct helpfile_table table
;
4807 irc_make_chanmode(channel
, modes
);
4809 reply("CSMSG_PEEK_INFO", channel
->name
);
4810 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4812 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4813 reply("CSMSG_PEEK_MODES", modes
);
4814 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4818 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4819 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4820 for(n
= 0; n
< channel
->members
.used
; n
++)
4822 mn
= channel
->members
.list
[n
];
4823 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4825 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4826 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4831 reply("CSMSG_PEEK_OPS");
4832 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4835 reply("CSMSG_PEEK_NO_OPS");
4836 reply("CSMSG_PEEK_END");
4840 static MODCMD_FUNC(cmd_wipeinfo
)
4842 struct handle_info
*victim
;
4843 struct userData
*ud
, *actor
;
4846 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4847 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4849 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4851 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4854 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4856 reply("MSG_USER_OUTRANKED", victim
->handle
);
4862 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4867 resync_channel(struct chanNode
*channel
)
4869 struct mod_chanmode
*changes
;
4870 struct chanData
*cData
= channel
->channel_info
;
4871 unsigned int ii
, used
;
4873 /* 6 = worst case -ovh+ovh on everyone */
4874 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4875 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4877 struct modeNode
*mn
= channel
->members
.list
[ii
];
4878 struct userData
*uData
;
4880 if(IsService(mn
->user
))
4884 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4886 /* If the channel is in no-mode mode, de-mode EVERYONE */
4887 if(cData
->chOpts
[chAutomode
] == 'n')
4891 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4892 changes
->args
[used
++].u
.member
= mn
;
4895 else /* Give various userlevels their modes.. */
4897 if(uData
&& uData
->access
>= UL_OP
)
4899 if(!(mn
->modes
& MODE_CHANOP
))
4901 changes
->args
[used
].mode
= MODE_CHANOP
;
4902 changes
->args
[used
++].u
.member
= mn
;
4905 else if(uData
&& uData
->access
>= UL_HALFOP
)
4907 if(mn
->modes
& MODE_CHANOP
)
4909 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4910 changes
->args
[used
++].u
.member
= mn
;
4912 if(!(mn
->modes
& MODE_HALFOP
))
4914 changes
->args
[used
].mode
= MODE_HALFOP
;
4915 changes
->args
[used
++].u
.member
= mn
;
4918 else if(uData
&& uData
->access
>= UL_PEON
)
4920 if(mn
->modes
& MODE_CHANOP
)
4922 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4923 changes
->args
[used
++].u
.member
= mn
;
4925 if(mn
->modes
& MODE_HALFOP
)
4927 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4928 changes
->args
[used
++].u
.member
= mn
;
4930 /* Don't voice peons if were in mode m */
4931 if( cData
->chOpts
[chAutomode
] == 'm')
4933 if(mn
->modes
& MODE_VOICE
)
4935 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4936 changes
->args
[used
++].u
.member
= mn
;
4939 /* otherwise, make user they do have voice */
4940 else if(!(mn
->modes
& MODE_VOICE
))
4942 changes
->args
[used
].mode
= MODE_VOICE
;
4943 changes
->args
[used
++].u
.member
= mn
;
4946 else /* They arnt on the userlist.. */
4948 /* If we voice everyone, but they dont.. */
4949 if(cData
->chOpts
[chAutomode
] == 'v')
4951 /* Remove anything except v */
4952 if(mn
->modes
& ~MODE_VOICE
)
4954 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4955 changes
->args
[used
++].u
.member
= mn
;
4958 if(!(mn
->modes
& MODE_VOICE
))
4960 changes
->args
[used
].mode
= MODE_VOICE
;
4961 changes
->args
[used
++].u
.member
= mn
;
4964 /* If we hop everyone, but they dont.. */
4965 else if(cData
->chOpts
[chAutomode
] == 'h')
4967 /* Remove anything except h */
4968 if(mn
->modes
& ~MODE_HALFOP
)
4970 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4971 changes
->args
[used
++].u
.member
= mn
;
4974 if(!(mn
->modes
& MODE_HALFOP
))
4976 changes
->args
[used
].mode
= MODE_HALFOP
;
4977 changes
->args
[used
++].u
.member
= mn
;
4980 /* If we op everyone, but they dont.. */
4981 else if(cData
->chOpts
[chAutomode
] == 'o')
4983 /* Remove anything except h */
4984 if(mn
->modes
& ~MODE_CHANOP
)
4986 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4987 changes
->args
[used
++].u
.member
= mn
;
4990 if(!(mn
->modes
& MODE_CHANOP
))
4992 changes
->args
[used
].mode
= MODE_CHANOP
;
4993 changes
->args
[used
++].u
.member
= mn
;
4996 /* they have no excuse for having modes, de-everything them */
5001 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
5002 changes
->args
[used
++].u
.member
= mn
;
5008 changes
->argc
= used
;
5009 mod_chanmode_announce(chanserv
, channel
, changes
);
5010 mod_chanmode_free(changes
);
5013 static CHANSERV_FUNC(cmd_resync
)
5015 resync_channel(channel
);
5016 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5020 static CHANSERV_FUNC(cmd_seen
)
5022 struct userData
*uData
;
5023 struct handle_info
*handle
;
5024 char seen
[INTERVALLEN
];
5028 if(!irccasecmp(argv
[1], chanserv
->nick
))
5030 reply("CSMSG_IS_CHANSERV");
5034 if(!(handle
= get_handle_info(argv
[1])))
5036 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5040 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5042 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5047 reply("CSMSG_USER_PRESENT", handle
->handle
);
5048 else if(uData
->seen
)
5049 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5051 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5053 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5054 reply("CSMSG_USER_VACATION", handle
->handle
);
5059 static MODCMD_FUNC(cmd_names
)
5061 struct userNode
*targ
;
5062 struct userData
*targData
;
5063 unsigned int ii
, pos
;
5066 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5068 targ
= channel
->members
.list
[ii
]->user
;
5069 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5072 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5075 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5079 if(IsUserSuspended(targData
))
5081 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5084 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5085 reply("CSMSG_END_NAMES", channel
->name
);
5090 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5092 switch(ntype
->visible_type
)
5094 case NOTE_VIS_ALL
: return 1;
5095 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5096 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5101 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5103 struct userData
*uData
;
5105 switch(ntype
->set_access_type
)
5107 case NOTE_SET_CHANNEL_ACCESS
:
5108 if(!user
->handle_info
)
5110 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5112 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5113 case NOTE_SET_CHANNEL_SETTER
:
5114 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5115 case NOTE_SET_PRIVILEGED
: default:
5116 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5120 static CHANSERV_FUNC(cmd_note
)
5122 struct chanData
*cData
;
5124 struct note_type
*ntype
;
5126 cData
= channel
->channel_info
;
5129 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5133 /* If no arguments, show all visible notes for the channel. */
5139 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5141 note
= iter_data(it
);
5142 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5145 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5146 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5149 reply("CSMSG_NOTELIST_END", channel
->name
);
5151 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5153 /* If one argument, show the named note. */
5156 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5157 && note_type_visible_to_user(cData
, note
->type
, user
))
5159 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5161 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5162 && note_type_visible_to_user(NULL
, ntype
, user
))
5164 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5169 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5173 /* Assume they're trying to set a note. */
5177 ntype
= dict_find(note_types
, argv
[1], NULL
);
5180 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5183 else if(note_type_settable_by_user(channel
, ntype
, user
))
5185 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5186 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5187 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5188 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5189 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5191 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5193 /* The note is viewable to staff only, so return 0
5194 to keep the invocation from getting logged (or
5195 regular users can see it in !events). */
5201 reply("CSMSG_NO_ACCESS");
5208 static CHANSERV_FUNC(cmd_delnote
)
5213 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5214 || !note_type_settable_by_user(channel
, note
->type
, user
))
5216 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5219 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5220 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5224 static CHANSERV_FUNC(cmd_last
)
5230 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5232 if(numoflines
< 1 || numoflines
> 200)
5234 reply("CSMSG_LAST_INVALID");
5237 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5241 static CHANSERV_FUNC(cmd_events
)
5243 struct logSearch discrim
;
5244 struct logReport report
;
5245 unsigned int matches
, limit
;
5247 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5248 if(limit
< 1 || limit
> 200)
5251 memset(&discrim
, 0, sizeof(discrim
));
5252 discrim
.masks
.bot
= chanserv
;
5253 discrim
.masks
.channel_name
= channel
->name
;
5255 discrim
.masks
.command
= argv
[2];
5256 discrim
.limit
= limit
;
5257 discrim
.max_time
= INT_MAX
;
5258 discrim
.severities
= 1 << LOG_COMMAND
;
5259 report
.reporter
= chanserv
;
5261 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5262 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5264 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5266 reply("MSG_MATCH_COUNT", matches
);
5268 reply("MSG_NO_MATCHES");
5272 static CHANSERV_FUNC(cmd_say
)
5278 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5279 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5281 else if(GetUserH(argv
[1]))
5284 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5285 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5289 reply("MSG_NOT_TARGET_NAME");
5295 static CHANSERV_FUNC(cmd_emote
)
5301 /* CTCP is so annoying. */
5302 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5303 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5305 else if(GetUserH(argv
[1]))
5307 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5308 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5312 reply("MSG_NOT_TARGET_NAME");
5318 struct channelList
*
5319 chanserv_support_channels(void)
5321 return &chanserv_conf
.support_channels
;
5324 static CHANSERV_FUNC(cmd_expire
)
5326 int channel_count
= registered_channels
;
5327 expire_channels(NULL
);
5328 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5333 chanserv_expire_suspension(void *data
)
5335 struct suspended
*suspended
= data
;
5336 struct chanNode
*channel
;
5338 if(!suspended
->expires
|| (now
< suspended
->expires
))
5339 suspended
->revoked
= now
;
5340 channel
= suspended
->cData
->channel
;
5341 suspended
->cData
->channel
= channel
;
5342 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5343 if(!IsOffChannel(suspended
->cData
))
5345 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5346 ss_cs_join_channel(channel
, 1);
5350 static CHANSERV_FUNC(cmd_csuspend
)
5352 struct suspended
*suspended
;
5353 char reason
[MAXLEN
];
5354 time_t expiry
, duration
;
5355 struct userData
*uData
;
5359 if(IsProtected(channel
->channel_info
))
5361 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5365 if(argv
[1][0] == '!')
5367 else if(IsSuspended(channel
->channel_info
))
5369 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5370 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5374 if(!strcmp(argv
[1], "0"))
5376 else if((duration
= ParseInterval(argv
[1])))
5377 expiry
= now
+ duration
;
5380 reply("MSG_INVALID_DURATION", argv
[1]);
5384 unsplit_string(argv
+ 2, argc
- 2, reason
);
5386 suspended
= calloc(1, sizeof(*suspended
));
5387 suspended
->revoked
= 0;
5388 suspended
->issued
= now
;
5389 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5390 suspended
->expires
= expiry
;
5391 suspended
->reason
= strdup(reason
);
5392 suspended
->cData
= channel
->channel_info
;
5393 suspended
->previous
= suspended
->cData
->suspended
;
5394 suspended
->cData
->suspended
= suspended
;
5396 if(suspended
->expires
)
5397 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5399 if(IsSuspended(channel
->channel_info
))
5401 suspended
->previous
->revoked
= now
;
5402 if(suspended
->previous
->expires
)
5403 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5404 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5405 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5409 /* Mark all users in channel as absent. */
5410 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5419 /* Mark the channel as suspended, then part. */
5420 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5421 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5422 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5423 reply("CSMSG_SUSPENDED", channel
->name
);
5424 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5425 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5430 static CHANSERV_FUNC(cmd_cunsuspend
)
5432 struct suspended
*suspended
;
5433 char message
[MAXLEN
];
5435 if(!IsSuspended(channel
->channel_info
))
5437 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5441 suspended
= channel
->channel_info
->suspended
;
5443 /* Expire the suspension and join ChanServ to the channel. */
5444 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5445 chanserv_expire_suspension(suspended
);
5446 reply("CSMSG_UNSUSPENDED", channel
->name
);
5447 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5448 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5452 typedef struct chanservSearch
5460 unsigned long flags
;
5464 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5467 chanserv_search_create(struct svccmd
*cmd
, struct userNode
*user
, unsigned int argc
, char *argv
[])
5472 search
= malloc(sizeof(struct chanservSearch
));
5473 memset(search
, 0, sizeof(*search
));
5476 for(i
= 0; i
< argc
; i
++)
5478 /* Assume all criteria require arguments. */
5481 reply("MSG_MISSING_PARAMS", argv
[i
]);
5485 if(!irccasecmp(argv
[i
], "name"))
5486 search
->name
= argv
[++i
];
5487 else if(!irccasecmp(argv
[i
], "registrar"))
5488 search
->registrar
= argv
[++i
];
5489 else if(!irccasecmp(argv
[i
], "unvisited"))
5490 search
->unvisited
= ParseInterval(argv
[++i
]);
5491 else if(!irccasecmp(argv
[i
], "registered"))
5492 search
->registered
= ParseInterval(argv
[++i
]);
5493 else if(!irccasecmp(argv
[i
], "flags"))
5496 if(!irccasecmp(argv
[i
], "nodelete"))
5497 search
->flags
|= CHANNEL_NODELETE
;
5498 else if(!irccasecmp(argv
[i
], "suspended"))
5499 search
->flags
|= CHANNEL_SUSPENDED
;
5502 reply("CSMSG_INVALID_CFLAG", argv
[i
]);
5506 else if(!irccasecmp(argv
[i
], "limit"))
5507 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5510 reply("MSG_INVALID_CRITERIA", argv
[i
]);
5515 if(search
->name
&& !strcmp(search
->name
, "*"))
5517 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5518 search
->registrar
= 0;
5527 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5529 const char *name
= channel
->channel
->name
;
5530 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5531 (search
->registrar
&& !channel
->registrar
) ||
5532 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5533 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5534 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5535 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5542 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5544 struct chanData
*channel
;
5545 unsigned int matches
= 0;
5547 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5549 if(!chanserv_channel_match(channel
, search
))
5559 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5564 search_print(struct chanData
*channel
, void *data
)
5566 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5569 static CHANSERV_FUNC(cmd_search
)
5572 unsigned int matches
;
5573 channel_search_func action
;
5577 if(!irccasecmp(argv
[1], "count"))
5578 action
= search_count
;
5579 else if(!irccasecmp(argv
[1], "print"))
5580 action
= search_print
;
5583 reply("CSMSG_ACTION_INVALID", argv
[1]);
5587 search
= chanserv_search_create(cmd
, user
, argc
- 2, argv
+ 2);
5591 if(action
== search_count
)
5592 search
->limit
= INT_MAX
;
5594 if(action
== search_print
)
5596 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5597 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5601 matches
= chanserv_channel_search(search
, action
, user
);
5604 reply("MSG_MATCH_COUNT", matches
);
5606 reply("MSG_NO_MATCHES");
5612 static CHANSERV_FUNC(cmd_unvisited
)
5614 struct chanData
*cData
;
5615 time_t interval
= chanserv_conf
.channel_expire_delay
;
5616 char buffer
[INTERVALLEN
];
5617 unsigned int limit
= 25, matches
= 0;
5621 interval
= ParseInterval(argv
[1]);
5623 limit
= atoi(argv
[2]);
5626 intervalString(buffer
, interval
, user
->handle_info
);
5627 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5629 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5631 if((now
- cData
->visited
) < interval
)
5634 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5635 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5642 static MODCMD_FUNC(chan_opt_defaulttopic
)
5648 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5650 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5654 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5656 free(channel
->channel_info
->topic
);
5657 if(topic
[0] == '*' && topic
[1] == 0)
5659 topic
= channel
->channel_info
->topic
= NULL
;
5663 topic
= channel
->channel_info
->topic
= strdup(topic
);
5664 if(channel
->channel_info
->topic_mask
5665 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5666 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5668 SetChannelTopic(channel
, chanserv
, user
, topic
? topic
: "", 1);
5671 if(channel
->channel_info
->topic
)
5672 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5674 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5678 static MODCMD_FUNC(chan_opt_topicmask
)
5682 struct chanData
*cData
= channel
->channel_info
;
5685 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5687 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5691 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5693 if(cData
->topic_mask
)
5694 free(cData
->topic_mask
);
5695 if(mask
[0] == '*' && mask
[1] == 0)
5697 cData
->topic_mask
= 0;
5701 cData
->topic_mask
= strdup(mask
);
5703 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5704 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5705 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5709 if(channel
->channel_info
->topic_mask
)
5710 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5712 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5716 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5720 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5724 if(greeting
[0] == '*' && greeting
[1] == 0)
5728 unsigned int length
= strlen(greeting
);
5729 if(length
> chanserv_conf
.greeting_length
)
5731 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5734 *data
= strdup(greeting
);
5743 reply(name
, user_find_message(user
, "MSG_NONE"));
5747 static MODCMD_FUNC(chan_opt_greeting
)
5749 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5752 static MODCMD_FUNC(chan_opt_usergreeting
)
5754 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5757 static MODCMD_FUNC(chan_opt_modes
)
5759 struct mod_chanmode
*new_modes
;
5760 char modes
[MODELEN
];
5764 if (checkDefCon(DEFCON_NO_MODE_CHANGE
) && !IsOper(user
)) {
5765 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5769 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5771 reply("CSMSG_NO_ACCESS");
5774 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5776 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5778 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5780 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5783 else if(new_modes
->argc
> 1)
5785 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5786 mod_chanmode_free(new_modes
);
5791 channel
->channel_info
->modes
= *new_modes
;
5792 modcmd_chanmode_announce(new_modes
);
5793 mod_chanmode_free(new_modes
);
5797 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5799 reply("CSMSG_SET_MODES", modes
);
5801 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5805 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5807 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5809 struct chanData
*cData
= channel
->channel_info
;
5814 /* Set flag according to value. */
5815 if(enabled_string(argv
[1]))
5817 cData
->flags
|= mask
;
5820 else if(disabled_string(argv
[1]))
5822 cData
->flags
&= ~mask
;
5827 reply("MSG_INVALID_BINARY", argv
[1]);
5833 /* Find current option value. */
5834 value
= (cData
->flags
& mask
) ? 1 : 0;
5838 reply(name
, user_find_message(user
, "MSG_ON"));
5840 reply(name
, user_find_message(user
, "MSG_OFF"));
5844 static MODCMD_FUNC(chan_opt_nodelete
)
5846 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5848 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5852 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5855 static MODCMD_FUNC(chan_opt_dynlimit
)
5857 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5860 static MODCMD_FUNC(chan_opt_offchannel
)
5862 struct chanData
*cData
= channel
->channel_info
;
5867 /* Set flag according to value. */
5868 if(enabled_string(argv
[1]))
5870 if(!IsOffChannel(cData
))
5871 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5872 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5875 else if(disabled_string(argv
[1]))
5877 if(IsOffChannel(cData
))
5879 struct mod_chanmode change
;
5880 mod_chanmode_init(&change
);
5882 change
.args
[0].mode
= MODE_CHANOP
;
5883 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5884 mod_chanmode_announce(chanserv
, channel
, &change
);
5886 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5891 reply("MSG_INVALID_BINARY", argv
[1]);
5897 /* Find current option value. */
5898 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5902 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5904 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5908 static MODCMD_FUNC(chan_opt_defaults
)
5910 struct userData
*uData
;
5911 struct chanData
*cData
;
5912 const char *confirm
;
5913 enum levelOption lvlOpt
;
5914 enum charOption chOpt
;
5916 cData
= channel
->channel_info
;
5917 uData
= GetChannelUser(cData
, user
->handle_info
);
5918 if(!uData
|| (uData
->access
< UL_OWNER
))
5920 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5923 confirm
= make_confirmation_string(uData
);
5924 if((argc
< 2) || strcmp(argv
[1], confirm
))
5926 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5929 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5930 cData
->modes
= chanserv_conf
.default_modes
;
5931 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5932 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5933 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5934 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5935 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5940 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5942 struct chanData
*cData
= channel
->channel_info
;
5943 struct userData
*uData
;
5944 unsigned short value
;
5948 if(!check_user_level(channel
, user
, option
, 1, 1))
5950 reply("CSMSG_CANNOT_SET");
5953 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5954 if(!value
&& strcmp(argv
[1], "0"))
5956 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5959 uData
= GetChannelUser(cData
, user
->handle_info
);
5960 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5962 reply("CSMSG_BAD_SETLEVEL");
5968 /* This test only applies to owners, since non-owners
5969 * trying to set an option to above their level get caught
5970 * by the CSMSG_BAD_SETLEVEL test above.
5972 if(value
> uData
->access
)
5974 reply("CSMSG_BAD_SETTERS");
5981 cData
->lvlOpts
[option
] = value
;
5983 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5987 static MODCMD_FUNC(chan_opt_enfops
)
5989 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5992 static MODCMD_FUNC(chan_opt_enfhalfops
)
5994 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5996 static MODCMD_FUNC(chan_opt_enfmodes
)
5998 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
6001 static MODCMD_FUNC(chan_opt_enftopic
)
6003 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
6006 static MODCMD_FUNC(chan_opt_pubcmd
)
6008 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
6011 static MODCMD_FUNC(chan_opt_setters
)
6013 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
6016 static MODCMD_FUNC(chan_opt_userinfo
)
6018 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6021 static MODCMD_FUNC(chan_opt_topicsnarf
)
6023 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6026 static MODCMD_FUNC(chan_opt_inviteme
)
6028 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6031 /* TODO: Make look like this when no args are
6033 * -X3- -------------------------------
6034 * -X3- BanTimeout: Bans are removed:
6035 * -X3- ----- * indicates current -----
6036 * -X3- 0: [*] Never.
6037 * -X3- 1: [ ] After 10 minutes.
6038 * -X3- 2: [ ] After 2 hours.
6039 * -X3- 3: [ ] After 4 hours.
6040 * -X3- 4: [ ] After 24 hours.
6041 * -X3- 5: [ ] After one week.
6042 * -X3- ------------- End -------------
6045 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6047 struct chanData
*cData
= channel
->channel_info
;
6048 int count
= charOptions
[option
].count
, index
;
6052 index
= atoi(argv
[1]);
6054 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6056 reply("CSMSG_INVALID_NUMERIC", index
);
6057 /* Show possible values. */
6058 for(index
= 0; index
< count
; index
++)
6059 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6063 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6067 /* Find current option value. */
6070 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6074 /* Somehow, the option value is corrupt; reset it to the default. */
6075 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6080 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6084 static MODCMD_FUNC(chan_opt_automode
)
6086 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6089 static MODCMD_FUNC(chan_opt_protect
)
6091 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6094 static MODCMD_FUNC(chan_opt_toys
)
6096 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6099 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6101 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6104 static MODCMD_FUNC(chan_opt_bantimeout
)
6106 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6109 static MODCMD_FUNC(chan_opt_topicrefresh
)
6111 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6114 static MODCMD_FUNC(chan_opt_resync
)
6116 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6119 static struct svccmd_list set_shows_list
;
6122 handle_svccmd_unbind(struct svccmd
*target
) {
6124 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6125 if(target
== set_shows_list
.list
[ii
])
6126 set_shows_list
.used
= 0;
6129 static CHANSERV_FUNC(cmd_set
)
6131 struct svccmd
*subcmd
;
6135 /* Check if we need to (re-)initialize set_shows_list. */
6136 if(!set_shows_list
.used
)
6138 if(!set_shows_list
.size
)
6140 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6141 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6143 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6145 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6146 sprintf(buf
, "%s %s", argv
[0], name
);
6147 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6150 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6153 svccmd_list_append(&set_shows_list
, subcmd
);
6159 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6160 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6162 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6164 subcmd
= set_shows_list
.list
[ii
];
6165 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6167 reply("CSMSG_CHANNEL_OPTIONS_END");
6171 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6172 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6175 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6178 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6180 reply("CSMSG_NO_ACCESS");
6184 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6188 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6190 struct userData
*uData
;
6192 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6195 reply("CSMSG_NOT_USER", channel
->name
);
6201 /* Just show current option value. */
6203 else if(enabled_string(argv
[1]))
6205 uData
->flags
|= mask
;
6207 else if(disabled_string(argv
[1]))
6209 uData
->flags
&= ~mask
;
6213 reply("MSG_INVALID_BINARY", argv
[1]);
6217 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6221 static MODCMD_FUNC(user_opt_autoop
)
6223 struct userData
*uData
;
6225 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6228 reply("CSMSG_NOT_USER", channel
->name
);
6231 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6232 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6234 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6235 /* TODO: add halfops error message? or is the op one generic enough? */
6238 static MODCMD_FUNC(user_opt_autoinvite
)
6240 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6243 static MODCMD_FUNC(user_opt_info
)
6245 struct userData
*uData
;
6248 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6252 /* If they got past the command restrictions (which require access)
6253 * but fail this test, we have some fool with security override on.
6255 reply("CSMSG_NOT_USER", channel
->name
);
6262 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6263 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6265 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6268 bp
= strcspn(infoline
, "\001");
6271 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6276 if(infoline
[0] == '*' && infoline
[1] == 0)
6279 uData
->info
= strdup(infoline
);
6282 reply("CSMSG_USET_INFO", uData
->info
);
6284 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6288 struct svccmd_list uset_shows_list
;
6290 static CHANSERV_FUNC(cmd_uset
)
6292 struct svccmd
*subcmd
;
6296 /* Check if we need to (re-)initialize uset_shows_list. */
6297 if(!uset_shows_list
.used
)
6301 "AutoOp", "AutoInvite", "Info"
6304 if(!uset_shows_list
.size
)
6306 uset_shows_list
.size
= ArrayLength(options
);
6307 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6309 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6311 const char *name
= options
[ii
];
6312 sprintf(buf
, "%s %s", argv
[0], name
);
6313 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6316 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6319 svccmd_list_append(&uset_shows_list
, subcmd
);
6325 /* Do this so options are presented in a consistent order. */
6326 reply("CSMSG_USER_OPTIONS");
6327 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6328 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6332 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6333 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6336 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6340 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6343 static CHANSERV_FUNC(cmd_giveownership
)
6345 struct handle_info
*new_owner_hi
;
6346 struct userData
*new_owner
, *curr_user
;
6347 struct chanData
*cData
= channel
->channel_info
;
6348 struct do_not_register
*dnr
;
6349 struct giveownership
*giveownership
;
6350 unsigned int force
, override
;
6351 unsigned short co_access
, new_owner_old_access
;
6352 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6355 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6356 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6358 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6359 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6360 && (uData
->access
> 500)
6361 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6362 || uData
->access
< 500));
6365 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6367 struct userData
*owner
= NULL
;
6368 for(curr_user
= channel
->channel_info
->users
;
6370 curr_user
= curr_user
->next
)
6372 if(curr_user
->access
!= UL_OWNER
)
6376 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6383 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6385 char delay
[INTERVALLEN
];
6386 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6387 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6390 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6392 if(new_owner_hi
== user
->handle_info
)
6394 reply("CSMSG_NO_TRANSFER_SELF");
6397 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6402 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6406 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6410 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6412 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6415 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6416 if(!IsHelping(user
))
6417 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6419 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6423 new_owner_old_access
= new_owner
->access
;
6424 if(new_owner
->access
>= UL_COOWNER
)
6425 co_access
= new_owner
->access
;
6427 co_access
= UL_COOWNER
;
6428 new_owner
->access
= UL_OWNER
;
6430 curr_user
->access
= co_access
;
6431 cData
->ownerTransfer
= now
;
6433 giveownership
= calloc(1, sizeof(*giveownership
));
6434 giveownership
->issued
= now
;
6435 giveownership
->old_owner
= curr_user
->handle
->handle
;
6436 giveownership
->target
= new_owner_hi
->handle
;
6437 giveownership
->target_access
= new_owner_old_access
;
6440 if(argc
> (2 + force
))
6442 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6443 giveownership
->reason
= strdup(transfer_reason
);
6445 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6448 giveownership
->previous
= channel
->channel_info
->giveownership
;
6449 channel
->channel_info
->giveownership
= giveownership
;
6451 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6452 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6453 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6458 chanserv_expire_user_suspension(void *data
)
6460 struct userData
*target
= data
;
6462 target
->expires
= 0;
6463 target
->flags
&= ~USER_SUSPENDED
;
6466 static CHANSERV_FUNC(cmd_suspend
)
6468 struct handle_info
*hi
;
6469 struct userData
*self
, *target
;
6473 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6474 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6475 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6477 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6480 if(target
->access
>= self
->access
)
6482 reply("MSG_USER_OUTRANKED", hi
->handle
);
6485 if(target
->flags
& USER_SUSPENDED
)
6487 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6492 target
->present
= 0;
6495 if(!strcmp(argv
[2], "0"))
6499 unsigned int duration
;
6500 if(!(duration
= ParseInterval(argv
[2])))
6502 reply("MSG_INVALID_DURATION", argv
[2]);
6505 expiry
= now
+ duration
;
6508 target
->expires
= expiry
;
6511 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6513 target
->flags
|= USER_SUSPENDED
;
6514 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6518 static CHANSERV_FUNC(cmd_unsuspend
)
6520 struct handle_info
*hi
;
6521 struct userData
*self
, *target
;
6524 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6525 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6526 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6528 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6531 if(target
->access
>= self
->access
)
6533 reply("MSG_USER_OUTRANKED", hi
->handle
);
6536 if(!(target
->flags
& USER_SUSPENDED
))
6538 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6541 target
->flags
&= ~USER_SUSPENDED
;
6542 scan_user_presence(target
, NULL
);
6543 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6544 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6548 static MODCMD_FUNC(cmd_deleteme
)
6550 struct handle_info
*hi
;
6551 struct userData
*target
;
6552 const char *confirm_string
;
6553 unsigned short access
;
6556 hi
= user
->handle_info
;
6557 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6559 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6562 if(target
->access
== UL_OWNER
)
6564 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6567 confirm_string
= make_confirmation_string(target
);
6568 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6570 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6573 access
= target
->access
;
6574 channel_name
= strdup(channel
->name
);
6575 del_channel_user(target
, 1);
6576 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6582 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6584 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6585 struct chanData
*cData
;
6588 for(cData
= channelList
; cData
; cData
= cData
->next
)
6590 if(IsSuspended(cData
))
6592 opt
= cData
->chOpts
[chTopicRefresh
];
6595 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6598 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6599 cData
->last_refresh
= refresh_num
;
6601 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6605 chanserv_auto_resync(UNUSED_ARG(void *data
))
6607 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6608 struct chanData
*cData
;
6611 for(cData
= channelList
; cData
; cData
= cData
->next
)
6613 if(IsSuspended(cData
)) continue;
6614 opt
= cData
->chOpts
[chResync
];
6615 if(opt
== 'n') continue;
6616 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6617 resync_channel(cData
->channel
);
6618 cData
->last_resync
= refresh_num
;
6620 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6623 static CHANSERV_FUNC(cmd_unf
)
6627 char response
[MAXLEN
];
6628 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6629 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6630 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6633 reply("CSMSG_UNF_RESPONSE");
6637 static CHANSERV_FUNC(cmd_ping
)
6641 char response
[MAXLEN
];
6642 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6643 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6644 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6647 reply("CSMSG_PING_RESPONSE");
6651 static CHANSERV_FUNC(cmd_wut
)
6655 char response
[MAXLEN
];
6656 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6657 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6658 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6661 reply("CSMSG_WUT_RESPONSE");
6666 static CHANSERV_FUNC(cmd_8ball
)
6668 unsigned int i
, j
, accum
;
6673 for(i
=1; i
<argc
; i
++)
6674 for(j
=0; argv
[i
][j
]; j
++)
6675 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6676 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6679 char response
[MAXLEN
];
6680 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6681 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6684 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6688 #else /* Use cool 8ball instead */
6690 void eightball(char *outcome
, int method
, unsigned int seed
)
6694 #define NUMOFCOLORS 18
6695 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6696 "white", "black", "grey", "brown",
6697 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6698 "fuchsia","turquoise","magenta", "cyan"};
6699 #define NUMOFLOCATIONS 50
6700 char balllocations
[50][55] = {
6701 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6702 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6703 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6704 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6705 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6706 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6707 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6708 "your bra", "your hair", "your bed", "the couch", "the wall",
6709 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6710 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6711 #define NUMOFPREPS 15
6712 char ballpreps
[50][50] = {
6713 "Near", "Somewhere near", "In", "In", "In",
6714 "In", "Hiding in", "Under", "Next to", "Over",
6715 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6716 #define NUMOFNUMS 34
6717 char ballnums
[50][50] = {
6718 "A hundred", "A thousand", "A few", "42",
6719 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6720 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6721 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6723 #define NUMOFMULTS 8
6724 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6727 * 0: normal (Not used in x3)
6734 if (method
== 1) /* A Color */
6738 answer
= (rand() % 12); /* Make sure this is the # of entries */
6741 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6743 case 1: strcpy(tmp
, "Sort of a light %s color.");
6745 case 2: strcpy(tmp
, "Dark and dreary %s.");
6747 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6749 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6751 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6753 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6755 case 10: strcpy(tmp
, "Solid %s.");
6757 case 11: strcpy(tmp
, "Transparent %s.");
6759 default: strcpy(outcome
, "An invalid random number was generated.");
6762 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6765 else if (method
== 2) /* Location */
6767 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6769 else if (method
== 3) /* Number of ___ */
6771 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6775 //Debug(DBGWARNING, "Error in 8ball.");
6780 static CHANSERV_FUNC(cmd_8ball
)
6782 char *word1
, *word2
, *word3
;
6783 static char eb
[MAXLEN
];
6784 unsigned int accum
, i
, j
;
6788 for(i
=1; i
<argc
; i
++)
6789 for(j
=0; argv
[i
][j
]; j
++)
6790 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6792 accum
+= time(NULL
)/3600;
6794 word2
= argc
>2?argv
[2]:"";
6795 word3
= argc
>3?argv
[3]:"";
6798 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6799 eightball(eb
, 1, accum
);
6800 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6801 eightball(eb
, 1, accum
);
6802 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6803 eightball(eb
, 1, accum
);
6804 /*** LOCATION *****/
6809 (strcasecmp(word1
, "where") == 0) &&
6810 (strcasecmp(word2
, "is") == 0)
6814 strcasecmp(word1
, "where's") == 0
6817 eightball(eb
, 2, accum
);
6819 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6820 eightball(eb
, 3, accum
);
6824 /* Generic 8ball question.. so pull from x3.conf srvx style */
6827 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6830 char response
[MAXLEN
];
6831 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6832 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6835 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6841 char response
[MAXLEN
];
6842 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6843 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6846 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6851 static CHANSERV_FUNC(cmd_d
)
6853 unsigned long sides
, count
, modifier
, ii
, total
;
6854 char response
[MAXLEN
], *sep
;
6858 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6868 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6869 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6873 else if((sep
[0] == '-') && isdigit(sep
[1]))
6874 modifier
= strtoul(sep
, NULL
, 10);
6875 else if((sep
[0] == '+') && isdigit(sep
[1]))
6876 modifier
= strtoul(sep
+1, NULL
, 10);
6883 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6888 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6891 for(total
= ii
= 0; ii
< count
; ++ii
)
6892 total
+= (rand() % sides
) + 1;
6895 if((count
> 1) || modifier
)
6897 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6898 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6902 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6903 sprintf(response
, fmt
, total
, sides
);
6906 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6908 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6912 static CHANSERV_FUNC(cmd_huggle
)
6914 /* CTCP must be via PRIVMSG, never notice */
6916 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6918 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6922 static CHANSERV_FUNC(cmd_calc
)
6924 char response
[MAXLEN
];
6927 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6930 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6932 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6937 chanserv_adjust_limit(void *data
)
6939 struct mod_chanmode change
;
6940 struct chanData
*cData
= data
;
6941 struct chanNode
*channel
= cData
->channel
;
6944 if(IsSuspended(cData
))
6947 cData
->limitAdjusted
= now
;
6948 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6949 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6951 if(limit
> cData
->modes
.new_limit
)
6952 limit
= cData
->modes
.new_limit
;
6953 else if(limit
== cData
->modes
.new_limit
)
6957 mod_chanmode_init(&change
);
6958 change
.modes_set
= MODE_LIMIT
;
6959 change
.new_limit
= limit
;
6960 mod_chanmode_announce(chanserv
, channel
, &change
);
6964 handle_new_channel(struct chanNode
*channel
)
6966 struct chanData
*cData
;
6968 if(!(cData
= channel
->channel_info
))
6971 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6972 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6974 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6975 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6978 /* Welcome to my worst nightmare. Warning: Read (or modify)
6979 the code below at your own risk. */
6981 handle_join(struct modeNode
*mNode
)
6983 struct mod_chanmode change
;
6984 struct userNode
*user
= mNode
->user
;
6985 struct chanNode
*channel
= mNode
->channel
;
6986 struct chanData
*cData
;
6987 struct userData
*uData
= NULL
;
6988 struct banData
*bData
;
6989 struct handle_info
*handle
;
6990 unsigned int modes
= 0, info
= 0;
6993 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6996 cData
= channel
->channel_info
;
6997 if(channel
->members
.used
> cData
->max
)
6998 cData
->max
= channel
->members
.used
;
7001 /* Check for bans. If they're joining through a ban, one of two
7003 * 1: Join during a netburst, by riding the break. Kick them
7004 * unless they have ops or voice in the channel.
7005 * 2: They're allowed to join through the ban (an invite in
7006 * ircu2.10, or a +e on Hybrid, or something).
7007 * If they're not joining through a ban, and the banlist is not
7008 * full, see if they're on the banlist for the channel. If so,
7011 if(user
->uplink
->burst
&& !mNode
->modes
)
7014 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
7016 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
7018 /* Riding a netburst. Naughty. */
7019 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7026 mod_chanmode_init(&change
);
7028 if(channel
->banlist
.used
< MAXBANS
)
7030 /* Not joining through a ban. */
7031 for(bData
= cData
->bans
;
7032 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7033 bData
= bData
->next
);
7037 char kick_reason
[MAXLEN
];
7038 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7040 bData
->triggered
= now
;
7041 if(bData
!= cData
->bans
)
7043 /* Shuffle the ban to the head of the list. */
7045 bData
->next
->prev
= bData
->prev
;
7047 bData
->prev
->next
= bData
->next
;
7050 bData
->next
= cData
->bans
;
7053 cData
->bans
->prev
= bData
;
7054 cData
->bans
= bData
;
7057 change
.args
[0].mode
= MODE_BAN
;
7058 change
.args
[0].u
.hostmask
= bData
->mask
;
7059 mod_chanmode_announce(chanserv
, channel
, &change
);
7060 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7065 /* ChanServ will not modify the limits in join-flooded channels.
7066 It will also skip DynLimit processing when the user (or srvx)
7067 is bursting in, because there are likely more incoming. */
7068 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7069 && !user
->uplink
->burst
7070 && !channel
->join_flooded
7071 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7073 /* The user count has begun "bumping" into the channel limit,
7074 so set a timer to raise the limit a bit. Any previous
7075 timers are removed so three incoming users within the delay
7076 results in one limit change, not three. */
7078 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7079 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7082 /* Give automodes exept during join-floods */
7083 if(!channel
->join_flooded
)
7085 if(cData
->chOpts
[chAutomode
] == 'v')
7086 modes
|= MODE_VOICE
;
7087 else if(cData
->chOpts
[chAutomode
] == 'h')
7088 modes
|= MODE_HALFOP
;
7089 else if(cData
->chOpts
[chAutomode
] == 'o')
7090 modes
|= MODE_CHANOP
;
7093 greeting
= cData
->greeting
;
7094 if(user
->handle_info
)
7096 handle
= user
->handle_info
;
7098 if(IsHelper(user
) && !IsHelping(user
))
7101 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7103 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7105 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7111 uData
= GetTrueChannelAccess(cData
, handle
);
7112 if(uData
&& !IsUserSuspended(uData
))
7114 /* non users getting automodes are handled above. */
7115 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7117 if(uData
->access
>= UL_OP
)
7118 modes
|= MODE_CHANOP
;
7119 else if(uData
->access
>= UL_HALFOP
)
7120 modes
|= MODE_HALFOP
;
7121 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7122 modes
|= MODE_VOICE
;
7124 if(uData
->access
>= UL_PRESENT
)
7125 cData
->visited
= now
;
7126 if(cData
->user_greeting
)
7127 greeting
= cData
->user_greeting
;
7129 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7130 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7138 /* If user joining normally (not during burst), apply op or voice,
7139 * and send greeting/userinfo as appropriate.
7141 if(!user
->uplink
->burst
)
7145 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7146 if(modes & MODE_CHANOP) {
7147 modes &= ~MODE_HALFOP;
7148 modes &= ~MODE_VOICE;
7151 change
.args
[0].mode
= modes
;
7152 change
.args
[0].u
.member
= mNode
;
7153 mod_chanmode_announce(chanserv
, channel
, &change
);
7156 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7158 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7164 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7166 struct mod_chanmode change
;
7167 struct userData
*channel
;
7168 unsigned int ii
, jj
, i
;
7170 if(!user
->handle_info
)
7173 mod_chanmode_init(&change
);
7175 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7177 struct chanNode
*cn
;
7178 struct modeNode
*mn
;
7179 if(IsUserSuspended(channel
)
7180 || IsSuspended(channel
->channel
)
7181 || !(cn
= channel
->channel
->channel
))
7184 mn
= GetUserMode(cn
, user
);
7187 if(!IsUserSuspended(channel
)
7188 && IsUserAutoInvite(channel
)
7189 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7191 && !user
->uplink
->burst
)
7192 irc_invite(chanserv
, user
, cn
);
7196 if(channel
->access
>= UL_PRESENT
)
7197 channel
->channel
->visited
= now
;
7199 if(IsUserAutoOp(channel
))
7201 if(channel
->access
>= UL_OP
)
7202 change
.args
[0].mode
= MODE_CHANOP
;
7203 else if(channel
->access
>= UL_HALFOP
)
7204 change
.args
[0].mode
= MODE_HALFOP
;
7205 else if(channel
->access
>= UL_PEON
)
7206 change
.args
[0].mode
= MODE_VOICE
;
7208 change
.args
[0].mode
= 0;
7209 change
.args
[0].u
.member
= mn
;
7210 if(change
.args
[0].mode
)
7211 mod_chanmode_announce(chanserv
, cn
, &change
);
7214 channel
->seen
= now
;
7215 channel
->present
= 1;
7218 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7220 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7221 struct banData
*ban
;
7223 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7224 || !channel
->channel_info
7225 || IsSuspended(channel
->channel_info
))
7227 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7228 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7230 if(jj
< channel
->banlist
.used
)
7232 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7234 char kick_reason
[MAXLEN
];
7235 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7237 change
.args
[0].mode
= MODE_BAN
;
7238 change
.args
[0].u
.hostmask
= ban
->mask
;
7239 mod_chanmode_announce(chanserv
, channel
, &change
);
7240 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7241 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7242 ban
->triggered
= now
;
7247 if(IsSupportHelper(user
))
7249 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7251 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7253 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7259 if (user
->handle_info
->ignores
->used
) {
7260 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7261 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7265 if (user
->handle_info
->epithet
)
7266 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7270 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7272 struct chanData
*cData
;
7273 struct userData
*uData
;
7275 cData
= mn
->channel
->channel_info
;
7276 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7279 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7281 /* Allow for a bit of padding so that the limit doesn't
7282 track the user count exactly, which could get annoying. */
7283 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7285 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7286 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7290 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7292 scan_user_presence(uData
, mn
->user
);
7296 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7298 unsigned int ii
, jj
;
7299 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7301 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7302 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7304 if(jj
< mn
->user
->channels
.used
)
7307 if(ii
== chanserv_conf
.support_channels
.used
)
7308 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7313 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7315 struct userData
*uData
;
7317 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7318 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7319 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7322 if(protect_user(victim
, kicker
, channel
->channel_info
))
7324 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7325 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7328 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7333 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7335 struct chanData
*cData
;
7337 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7340 cData
= channel
->channel_info
;
7341 if(bad_topic(channel
, user
, channel
->topic
))
7342 { /* User doesnt have privs to set topics. Undo it */
7343 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7344 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7347 /* If there is a topic mask set, and the new topic doesnt match,
7348 * set the topic to mask + new_topic */
7349 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7351 char new_topic
[TOPICLEN
+1];
7352 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7355 SetChannelTopic(channel
, chanserv
, user
, new_topic
, 1);
7356 /* and fall through to topicsnarf code below.. */
7358 else /* Topic couldnt fit into mask, was too long */
7360 SetChannelTopic(channel
, chanserv
, user
, old_topic
, 1);
7361 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7362 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7366 /* With topicsnarf, grab the topic and save it as the default topic. */
7367 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7370 cData
->topic
= strdup(channel
->topic
);
7376 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7378 struct mod_chanmode
*bounce
= NULL
;
7379 unsigned int bnc
, ii
;
7382 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7385 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7386 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7388 char correct
[MAXLEN
];
7389 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7390 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7391 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7393 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7395 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7397 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7398 if(!protect_user(victim
, user
, channel
->channel_info
))
7401 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7404 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7405 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7406 if(bounce
->args
[bnc
].u
.member
)
7410 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7411 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7413 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7415 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7417 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7418 if(IsService(victim
) || validate_op(NULL
, user
, channel
, (struct userNode
*)victim
))
7421 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7422 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7423 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7426 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7428 const char *ban
= change
->args
[ii
].u
.hostmask
;
7429 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7432 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7433 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7434 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7436 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7441 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7442 mod_chanmode_announce(chanserv
, channel
, bounce
);
7443 for(ii
= 0; ii
< change
->argc
; ++ii
)
7444 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7445 free((char*)bounce
->args
[ii
].u
.hostmask
);
7446 mod_chanmode_free(bounce
);
7451 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7453 struct chanNode
*channel
;
7454 struct banData
*bData
;
7455 struct mod_chanmode change
;
7456 unsigned int ii
, jj
;
7457 char kick_reason
[MAXLEN
];
7459 mod_chanmode_init(&change
);
7461 change
.args
[0].mode
= MODE_BAN
;
7462 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7464 channel
= user
->channels
.list
[ii
]->channel
;
7465 /* Need not check for bans if they're opped or voiced. */
7466 /* TODO: does this make sense in automode v, h, and o? *
7467 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7468 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7470 /* Need not check for bans unless channel registration is active. */
7471 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7473 /* Look for a matching ban already on the channel. */
7474 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7475 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7477 /* Need not act if we found one. */
7478 if(jj
< channel
->banlist
.used
)
7480 /* Look for a matching ban in this channel. */
7481 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7483 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7485 change
.args
[0].u
.hostmask
= bData
->mask
;
7486 mod_chanmode_announce(chanserv
, channel
, &change
);
7487 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7488 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7489 bData
->triggered
= now
;
7490 break; /* we don't need to check any more bans in the channel */
7495 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7497 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7501 dict_remove2(handle_dnrs
, old_handle
, 1);
7502 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7503 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7508 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7510 struct userNode
*h_user
;
7512 if(handle
->channels
)
7514 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7515 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7517 while(handle
->channels
)
7518 del_channel_user(handle
->channels
, 1);
7523 handle_server_link(UNUSED_ARG(struct server
*server
))
7525 struct chanData
*cData
;
7527 for(cData
= channelList
; cData
; cData
= cData
->next
)
7529 if(!IsSuspended(cData
))
7530 cData
->may_opchan
= 1;
7531 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7532 && !cData
->channel
->join_flooded
7533 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7534 < chanserv_conf
.adjust_threshold
))
7536 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7537 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7543 chanserv_conf_read(void)
7547 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7548 struct mod_chanmode
*change
;
7549 struct string_list
*strlist
;
7550 struct chanNode
*chan
;
7553 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7555 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7558 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7559 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7560 chanserv_conf
.support_channels
.used
= 0;
7561 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7563 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7565 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7568 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7570 channelList_append(&chanserv_conf
.support_channels
, chan
);
7573 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7576 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7579 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7581 channelList_append(&chanserv_conf
.support_channels
, chan
);
7583 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7584 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7585 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7586 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7587 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7588 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7589 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7590 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7591 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7592 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7593 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7594 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7595 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7596 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7597 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7598 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7599 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7600 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7601 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7602 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7603 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7604 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7605 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7606 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7607 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7609 NickChange(chanserv
, str
, 0);
7610 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7611 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7612 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7613 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7614 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7615 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7616 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7617 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7618 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7619 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7620 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7621 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7622 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7623 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7624 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7625 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7626 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7627 god_timeout
= str
? ParseInterval(str
) : 60*15;
7628 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7631 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7632 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7633 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7634 && (change
->argc
< 2))
7636 chanserv_conf
.default_modes
= *change
;
7637 mod_chanmode_free(change
);
7639 free_string_list(chanserv_conf
.set_shows
);
7640 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7642 strlist
= string_list_copy(strlist
);
7645 static const char *list
[] = {
7646 /* free form text */
7647 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7648 /* options based on user level */
7649 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7650 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7651 /* multiple choice options */
7652 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7653 /* binary options */
7654 "DynLimit", "NoDelete", "BanTimeout",
7659 strlist
= alloc_string_list(ArrayLength(list
)-1);
7660 for(ii
=0; list
[ii
]; ii
++)
7661 string_list_append(strlist
, strdup(list
[ii
]));
7663 chanserv_conf
.set_shows
= strlist
;
7664 /* We don't look things up now, in case the list refers to options
7665 * defined by modules initialized after this point. Just mark the
7666 * function list as invalid, so it will be initialized.
7668 set_shows_list
.used
= 0;
7669 free_string_list(chanserv_conf
.eightball
);
7670 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7673 strlist
= string_list_copy(strlist
);
7677 strlist
= alloc_string_list(4);
7678 string_list_append(strlist
, strdup("Yes."));
7679 string_list_append(strlist
, strdup("No."));
7680 string_list_append(strlist
, strdup("Maybe so."));
7682 chanserv_conf
.eightball
= strlist
;
7683 free_string_list(chanserv_conf
.old_ban_names
);
7684 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7686 strlist
= string_list_copy(strlist
);
7688 strlist
= alloc_string_list(2);
7689 chanserv_conf
.old_ban_names
= strlist
;
7690 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7691 off_channel
= str
? atoi(str
) : 0;
7695 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7698 struct note_type
*ntype
;
7701 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7703 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7706 if(!(ntype
= chanserv_create_note_type(key
)))
7708 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7712 /* Figure out set access */
7713 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7715 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7716 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7718 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7720 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7721 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7723 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7725 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7729 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7730 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7731 ntype
->set_access
.min_opserv
= 0;
7734 /* Figure out visibility */
7735 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7736 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7737 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7738 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7739 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7740 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7741 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7742 ntype
->visible_type
= NOTE_VIS_ALL
;
7744 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7746 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7747 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7751 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7753 struct handle_info
*handle
;
7754 struct userData
*uData
;
7755 char *seen
, *inf
, *flags
, *expires
;
7757 unsigned short access
;
7759 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7761 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7765 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7766 if(access
> UL_OWNER
)
7768 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7772 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7773 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7774 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7775 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7776 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7777 handle
= get_handle_info(key
);
7780 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7784 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7785 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7786 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7788 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7790 if(uData
->expires
> now
)
7791 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7793 uData
->flags
&= ~USER_SUSPENDED
;
7796 /* Upgrade: set autoop to the inverse of noautoop */
7797 if(chanserv_read_version
< 2)
7799 /* if noautoop is true, set autoop false, and vice versa */
7800 if(uData
->flags
& USER_NOAUTO_OP
)
7801 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7803 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7804 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
);
7810 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7812 struct banData
*bData
;
7813 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7814 time_t set_time
, triggered_time
, expires_time
;
7816 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7818 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7822 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7823 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7824 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7825 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7826 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7827 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7828 if (!reason
|| !owner
)
7831 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7832 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7834 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7836 expires_time
= set_time
+ atoi(s_duration
);
7840 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7843 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7846 static struct suspended
*
7847 chanserv_read_suspended(dict_t obj
)
7849 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7853 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7854 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7855 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7856 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7857 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7858 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7859 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7860 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7861 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7862 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7866 static struct giveownership
*
7867 chanserv_read_giveownership(dict_t obj
)
7869 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7873 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7874 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7876 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7878 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7879 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7881 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7882 giveownership
->reason
= str
? strdup(str
) : NULL
;
7883 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7884 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7886 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7887 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7888 return giveownership
;
7892 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7894 struct suspended
*suspended
;
7895 struct giveownership
*giveownership
;
7896 struct mod_chanmode
*modes
;
7897 struct chanNode
*cNode
;
7898 struct chanData
*cData
;
7899 struct dict
*channel
, *obj
;
7900 char *str
, *argv
[10];
7904 channel
= hir
->d
.object
;
7906 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7909 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7912 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7915 cData
= register_channel(cNode
, str
);
7918 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7922 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7924 enum levelOption lvlOpt
;
7925 enum charOption chOpt
;
7927 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7928 cData
->flags
= atoi(str
);
7930 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7932 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7934 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7935 else if(levelOptions
[lvlOpt
].old_flag
)
7937 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7938 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7940 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7944 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7946 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7948 cData
->chOpts
[chOpt
] = str
[0];
7951 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7953 enum levelOption lvlOpt
;
7954 enum charOption chOpt
;
7957 cData
->flags
= base64toint(str
, 5);
7958 count
= strlen(str
+= 5);
7959 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7962 if(levelOptions
[lvlOpt
].old_flag
)
7964 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7965 lvl
= levelOptions
[lvlOpt
].flag_value
;
7967 lvl
= levelOptions
[lvlOpt
].default_value
;
7969 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7971 case 'c': lvl
= UL_COOWNER
; break;
7972 case 'm': lvl
= UL_MANAGER
; break;
7973 case 'n': lvl
= UL_OWNER
+1; break;
7974 case 'o': lvl
= UL_OP
; break;
7975 case 'p': lvl
= UL_PEON
; break;
7976 case 'h': lvl
= UL_HALFOP
; break;
7977 case 'w': lvl
= UL_OWNER
; break;
7978 default: lvl
= 0; break;
7980 cData
->lvlOpts
[lvlOpt
] = lvl
;
7982 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7983 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7986 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7988 suspended
= chanserv_read_suspended(obj
);
7989 cData
->suspended
= suspended
;
7990 suspended
->cData
= cData
;
7991 /* We could use suspended->expires and suspended->revoked to
7992 * set the CHANNEL_SUSPENDED flag, but we don't. */
7994 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7996 suspended
= calloc(1, sizeof(*suspended
));
7997 suspended
->issued
= 0;
7998 suspended
->revoked
= 0;
7999 suspended
->suspender
= strdup(str
);
8000 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
8001 suspended
->expires
= str
? atoi(str
) : 0;
8002 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
8003 suspended
->reason
= strdup(str
? str
: "No reason");
8004 suspended
->previous
= NULL
;
8005 cData
->suspended
= suspended
;
8006 suspended
->cData
= cData
;
8010 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8011 suspended
= NULL
; /* to squelch a warning */
8014 if(IsSuspended(cData
)) {
8015 if(suspended
->expires
> now
)
8016 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
8017 else if(suspended
->expires
)
8018 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8021 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8023 giveownership
= chanserv_read_giveownership(obj
);
8024 cData
->giveownership
= giveownership
;
8027 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8028 struct mod_chanmode change
;
8029 mod_chanmode_init(&change
);
8031 change
.args
[0].mode
= MODE_CHANOP
;
8032 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8033 mod_chanmode_announce(chanserv
, cNode
, &change
);
8036 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8037 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8038 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8039 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8040 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8041 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8042 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8043 cData
->max
= str
? atoi(str
) : 0;
8044 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8045 cData
->greeting
= str
? strdup(str
) : NULL
;
8046 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8047 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8048 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8049 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8050 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8051 cData
->topic
= str
? strdup(str
) : NULL
;
8053 if(!IsSuspended(cData
)
8054 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8055 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8056 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8057 cData
->modes
= *modes
;
8059 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8060 if(cData
->modes
.argc
> 1)
8061 cData
->modes
.argc
= 1;
8062 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8063 mod_chanmode_free(modes
);
8066 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8067 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8068 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8070 if(!cData
->users
&& !IsProtected(cData
))
8072 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8073 unregister_channel(cData
, "has empty user list.");
8077 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8078 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8079 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8081 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8082 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8084 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8085 struct record_data
*rd
= iter_data(it
);
8086 const char *note
, *setter
;
8088 if(rd
->type
!= RECDB_OBJECT
)
8090 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8094 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8096 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8098 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8102 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8103 if(!setter
) setter
= "<unknown>";
8104 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8112 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8114 const char *setter
, *reason
, *str
;
8115 struct do_not_register
*dnr
;
8117 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8120 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8123 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8126 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8129 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8132 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8134 dnr
->set
= atoi(str
);
8140 chanserv_version_read(struct dict
*section
)
8144 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8146 chanserv_read_version
= atoi(str
);
8147 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8151 chanserv_saxdb_read(struct dict
*database
)
8153 struct dict
*section
;
8156 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8157 chanserv_version_read(section
);
8159 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8160 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8161 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8163 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8164 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8165 chanserv_channel_read(iter_key(it
), iter_data(it
));
8167 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8168 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8169 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8175 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8177 int high_present
= 0;
8178 saxdb_start_record(ctx
, KEY_USERS
, 1);
8179 for(; uData
; uData
= uData
->next
)
8181 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8183 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8184 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8185 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8187 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8189 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8191 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8192 saxdb_end_record(ctx
);
8194 saxdb_end_record(ctx
);
8195 return high_present
;
8199 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8203 saxdb_start_record(ctx
, KEY_BANS
, 1);
8204 for(; bData
; bData
= bData
->next
)
8206 saxdb_start_record(ctx
, bData
->mask
, 0);
8207 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8208 if(bData
->triggered
)
8209 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8211 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8213 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8215 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8216 saxdb_end_record(ctx
);
8218 saxdb_end_record(ctx
);
8222 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8224 saxdb_start_record(ctx
, name
, 0);
8225 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8226 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8228 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8230 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8232 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8234 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8235 saxdb_end_record(ctx
);
8239 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8241 saxdb_start_record(ctx
, name
, 0);
8242 if(giveownership
->staff_issuer
)
8243 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8244 if(giveownership
->old_owner
)
8245 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8246 if(giveownership
->target
)
8247 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8248 if(giveownership
->target_access
)
8249 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8250 if(giveownership
->reason
)
8251 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8252 if(giveownership
->issued
)
8253 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8254 if(giveownership
->previous
)
8255 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8256 saxdb_end_record(ctx
);
8260 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8264 enum levelOption lvlOpt
;
8265 enum charOption chOpt
;
8267 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8269 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8270 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8272 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8273 if(channel
->registrar
)
8274 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8275 if(channel
->greeting
)
8276 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8277 if(channel
->user_greeting
)
8278 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8279 if(channel
->topic_mask
)
8280 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8281 if(channel
->suspended
)
8282 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8283 if(channel
->giveownership
)
8284 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8286 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8287 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8288 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8289 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8290 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8292 buf
[0] = channel
->chOpts
[chOpt
];
8294 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8296 saxdb_end_record(ctx
);
8298 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8300 mod_chanmode_format(&channel
->modes
, buf
);
8301 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8304 high_present
= chanserv_write_users(ctx
, channel
->users
);
8305 chanserv_write_bans(ctx
, channel
->bans
);
8307 if(dict_size(channel
->notes
))
8311 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8312 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8314 struct note
*note
= iter_data(it
);
8315 saxdb_start_record(ctx
, iter_key(it
), 0);
8316 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8317 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8318 saxdb_end_record(ctx
);
8320 saxdb_end_record(ctx
);
8323 if(channel
->ownerTransfer
)
8324 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8325 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8326 saxdb_end_record(ctx
);
8330 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8334 saxdb_start_record(ctx
, ntype
->name
, 0);
8335 switch(ntype
->set_access_type
)
8337 case NOTE_SET_CHANNEL_ACCESS
:
8338 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8340 case NOTE_SET_CHANNEL_SETTER
:
8341 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8343 case NOTE_SET_PRIVILEGED
: default:
8344 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8347 switch(ntype
->visible_type
)
8349 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8350 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8351 case NOTE_VIS_PRIVILEGED
:
8352 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8354 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8355 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8356 saxdb_end_record(ctx
);
8360 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8362 struct do_not_register
*dnr
;
8365 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8367 dnr
= iter_data(it
);
8368 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8370 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8371 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8372 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8373 saxdb_end_record(ctx
);
8378 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8381 struct chanData
*channel
;
8383 /* Version Control*/
8384 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8385 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8386 saxdb_end_record(ctx
);
8389 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8390 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8391 chanserv_write_note_type(ctx
, iter_data(it
));
8392 saxdb_end_record(ctx
);
8395 saxdb_start_record(ctx
, KEY_DNR
, 1);
8396 write_dnrs_helper(ctx
, handle_dnrs
);
8397 write_dnrs_helper(ctx
, plain_dnrs
);
8398 write_dnrs_helper(ctx
, mask_dnrs
);
8399 saxdb_end_record(ctx
);
8402 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8403 for(channel
= channelList
; channel
; channel
= channel
->next
)
8404 chanserv_write_channel(ctx
, channel
);
8405 saxdb_end_record(ctx
);
8411 chanserv_db_cleanup(void) {
8413 unreg_part_func(handle_part
);
8415 unregister_channel(channelList
, "terminating.");
8416 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8417 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8418 free(chanserv_conf
.support_channels
.list
);
8419 dict_delete(handle_dnrs
);
8420 dict_delete(plain_dnrs
);
8421 dict_delete(mask_dnrs
);
8422 dict_delete(note_types
);
8423 free_string_list(chanserv_conf
.eightball
);
8424 free_string_list(chanserv_conf
.old_ban_names
);
8425 free_string_list(chanserv_conf
.set_shows
);
8426 free(set_shows_list
.list
);
8427 free(uset_shows_list
.list
);
8430 struct userData
*helper
= helperList
;
8431 helperList
= helperList
->next
;
8436 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8437 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8438 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8441 init_chanserv(const char *nick
)
8443 struct chanNode
*chan
;
8445 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8446 conf_register_reload(chanserv_conf_read
);
8448 reg_server_link_func(handle_server_link
);
8450 reg_new_channel_func(handle_new_channel
);
8451 reg_join_func(handle_join
);
8452 reg_part_func(handle_part
);
8453 reg_kick_func(handle_kick
);
8454 reg_topic_func(handle_topic
);
8455 reg_mode_change_func(handle_mode
);
8456 reg_nick_change_func(handle_nick_change
);
8458 reg_auth_func(handle_auth
);
8459 reg_handle_rename_func(handle_rename
);
8460 reg_unreg_func(handle_unreg
);
8462 handle_dnrs
= dict_new();
8463 dict_set_free_data(handle_dnrs
, free
);
8464 plain_dnrs
= dict_new();
8465 dict_set_free_data(plain_dnrs
, free
);
8466 mask_dnrs
= dict_new();
8467 dict_set_free_data(mask_dnrs
, free
);
8469 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8470 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8471 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8472 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8473 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8474 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8475 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8476 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8477 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8478 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8480 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8482 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8483 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8485 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8486 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8487 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8488 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8489 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8491 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8492 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8493 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8494 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8495 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8496 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8498 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8499 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8500 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8501 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8503 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8504 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8505 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8506 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8507 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8508 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8509 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8510 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8511 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8512 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8514 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8515 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8516 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8517 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8518 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8519 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8520 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8521 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8522 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8523 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8524 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8525 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8526 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8527 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8529 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8530 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8531 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8532 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8534 /* if you change dellamer access, see also places
8535 * like unbanme which have manager hardcoded. */
8536 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8537 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8539 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8541 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8543 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8544 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8545 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8546 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8547 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8548 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8549 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8550 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8551 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8552 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8553 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8554 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8556 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8557 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8559 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8560 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8561 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8562 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8564 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8565 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8566 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8567 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8568 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8570 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8571 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8572 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8573 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8574 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8575 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8576 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8578 /* Channel options */
8579 DEFINE_CHANNEL_OPTION(defaulttopic
);
8580 DEFINE_CHANNEL_OPTION(topicmask
);
8581 DEFINE_CHANNEL_OPTION(greeting
);
8582 DEFINE_CHANNEL_OPTION(usergreeting
);
8583 DEFINE_CHANNEL_OPTION(modes
);
8584 DEFINE_CHANNEL_OPTION(enfops
);
8585 DEFINE_CHANNEL_OPTION(enfhalfops
);
8586 DEFINE_CHANNEL_OPTION(automode
);
8587 DEFINE_CHANNEL_OPTION(protect
);
8588 DEFINE_CHANNEL_OPTION(enfmodes
);
8589 DEFINE_CHANNEL_OPTION(enftopic
);
8590 DEFINE_CHANNEL_OPTION(pubcmd
);
8591 DEFINE_CHANNEL_OPTION(userinfo
);
8592 DEFINE_CHANNEL_OPTION(dynlimit
);
8593 DEFINE_CHANNEL_OPTION(topicsnarf
);
8594 DEFINE_CHANNEL_OPTION(nodelete
);
8595 DEFINE_CHANNEL_OPTION(toys
);
8596 DEFINE_CHANNEL_OPTION(setters
);
8597 DEFINE_CHANNEL_OPTION(topicrefresh
);
8598 DEFINE_CHANNEL_OPTION(resync
);
8599 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8600 DEFINE_CHANNEL_OPTION(bantimeout
);
8601 DEFINE_CHANNEL_OPTION(inviteme
);
8603 DEFINE_CHANNEL_OPTION(offchannel
);
8604 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8606 /* Alias set topic to set defaulttopic for compatibility. */
8607 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8610 DEFINE_USER_OPTION(autoinvite
);
8611 DEFINE_USER_OPTION(info
);
8612 DEFINE_USER_OPTION(autoop
);
8614 /* Alias uset autovoice to uset autoop. */
8615 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8617 note_types
= dict_new();
8618 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8621 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8622 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8623 service_register(chanserv
)->trigger
= '!';
8624 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8627 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8629 if(chanserv_conf
.channel_expire_frequency
)
8630 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8632 if(chanserv_conf
.ban_timeout_frequency
)
8633 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8635 if(chanserv_conf
.refresh_period
)
8637 time_t next_refresh
;
8638 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8639 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8640 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8643 if (autojoin_channels
&& chanserv
) {
8644 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8645 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8646 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8650 reg_exit_func(chanserv_db_cleanup
);
8651 message_register_table(msgtab
);