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_ALREADY_PRESENT", "%s is already in $b%s$b." },
357 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
358 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
359 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
360 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
362 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
363 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
364 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
366 /* Channel userlist */
367 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
368 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
369 /* uncomment if needed to adujust styles (and change code below)
370 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
371 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
372 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
373 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
374 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
375 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
377 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
378 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
379 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
381 /* Channel note list */
382 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
383 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
384 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
385 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
386 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
387 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
388 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
389 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
390 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
391 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
392 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
393 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
394 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
395 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
396 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
398 /* Channel [un]suspension */
399 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
400 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
401 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
402 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
403 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
404 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
405 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
407 /* Access information */
408 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
409 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
410 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
411 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
412 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
413 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
414 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
415 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
416 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
417 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
418 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
420 /* Seen information */
421 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
422 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
423 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
424 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
426 /* Names information */
427 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
428 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
430 /* Channel information */
431 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
432 { "CSMSG_BAR", "----------------------------------------"},
433 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
434 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
435 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
436 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
437 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
438 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
439 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
440 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
441 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
442 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
443 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
444 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
445 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
446 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
447 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
451 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
452 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
453 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
454 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
455 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
456 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
457 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
458 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
460 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
461 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
462 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
463 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
464 { "CSMSG_PEEK_OPS", "$bOps:$b" },
465 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
466 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
468 /* Network information */
469 { "CSMSG_NETWORK_INFO", "Network Information:" },
470 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
471 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
472 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
473 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
474 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
475 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
476 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
477 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
480 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
481 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
482 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
484 /* Channel searches */
485 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
486 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
487 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
488 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
490 /* Channel configuration */
491 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
492 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
493 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
494 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
497 { "CSMSG_USER_OPTIONS", "User Options:" },
498 // { "CSMSG_USER_PROTECTED", "That user is protected." },
501 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
502 { "CSMSG_PING_RESPONSE", "Pong!" },
503 { "CSMSG_WUT_RESPONSE", "wut" },
504 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
505 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
506 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
507 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
508 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
509 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
510 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
513 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
514 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
518 /* eject_user and unban_user flags */
519 #define ACTION_KICK 0x0001
520 #define ACTION_BAN 0x0002
521 #define ACTION_ADD_LAMER 0x0004
522 #define ACTION_ADD_TIMED_LAMER 0x0008
523 #define ACTION_UNBAN 0x0010
524 #define ACTION_DEL_LAMER 0x0020
526 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
527 #define MODELEN 40 + KEYLEN
531 #define CSFUNC_ARGS user, channel, argc, argv, cmd
533 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
534 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
535 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
536 reply("MSG_MISSING_PARAMS", argv[0]); \
540 DECLARE_LIST(dnrList
, struct do_not_register
*);
541 DEFINE_LIST(dnrList
, struct do_not_register
*);
543 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
545 struct userNode
*chanserv
;
548 extern struct string_list
*autojoin_channels
;
549 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
550 static struct log_type
*CS_LOG
;
551 struct adduserPending
* adduser_pendings
= NULL
;
552 unsigned int adduser_pendings_count
= 0;
553 unsigned long god_timeout
;
557 struct channelList support_channels
;
558 struct mod_chanmode default_modes
;
560 unsigned long db_backup_frequency
;
561 unsigned long channel_expire_frequency
;
562 unsigned long ban_timeout_frequency
;
565 unsigned int adjust_delay
;
566 long channel_expire_delay
;
567 unsigned int nodelete_level
;
569 unsigned int adjust_threshold
;
570 int join_flood_threshold
;
572 unsigned int greeting_length
;
573 unsigned int refresh_period
;
574 unsigned int giveownership_period
;
576 unsigned int max_owned
;
577 unsigned int max_chan_users
;
578 unsigned int max_chan_bans
; /* lamers */
579 unsigned int max_userinfo_length
;
581 struct string_list
*set_shows
;
582 struct string_list
*eightball
;
583 struct string_list
*old_ban_names
;
585 const char *ctcp_short_ban_duration
;
586 const char *ctcp_long_ban_duration
;
588 const char *irc_operator_epithet
;
589 const char *network_helper_epithet
;
590 const char *support_helper_epithet
;
595 struct userNode
*user
;
596 struct userNode
*bot
;
597 struct chanNode
*channel
;
599 unsigned short lowest
;
600 unsigned short highest
;
601 struct userData
**users
;
602 struct helpfile_table table
;
605 enum note_access_type
607 NOTE_SET_CHANNEL_ACCESS
,
608 NOTE_SET_CHANNEL_SETTER
,
612 enum note_visible_type
615 NOTE_VIS_CHANNEL_USERS
,
621 enum note_access_type set_access_type
;
623 unsigned int min_opserv
;
624 unsigned short min_ulevel
;
626 enum note_visible_type visible_type
;
627 unsigned int max_length
;
634 struct note_type
*type
;
635 char setter
[NICKSERV_HANDLE_LEN
+1];
639 static unsigned int registered_channels
;
640 static unsigned int banCount
;
642 static const struct {
645 unsigned short level
;
647 } accessLevels
[] = { /* MUST be orderd less to most! */
648 { "peon", "Peon", UL_PEON
, '+' },
649 { "halfop", "HalfOp", UL_HALFOP
, '%' },
650 { "op", "Op", UL_OP
, '@' },
651 { "manager", "Manager", UL_MANAGER
, '%' },
652 { "coowner", "Coowner", UL_COOWNER
, '*' },
653 { "owner", "Owner", UL_OWNER
, '!' },
654 { "helper", "BUG:", UL_HELPER
, 'X' }
657 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
658 static const struct {
661 unsigned short default_value
;
662 unsigned int old_idx
;
663 unsigned int old_flag
;
664 unsigned short flag_value
;
666 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
667 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
668 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
669 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
670 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
671 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
672 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
673 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
674 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
677 struct charOptionValues
{
680 } automodeValues
[] = {
681 { 'n', "CSMSG_AUTOMODE_NONE" },
682 { 'y', "CSMSG_AUTOMODE_NORMAL" },
683 { 'v', "CSMSG_AUTOMODE_VOICE" },
684 { 'h', "CSMSG_AUTOMODE_HOP" },
685 { 'o', "CSMSG_AUTOMODE_OP" },
686 { 'm', "CSMSG_AUTOMODE_MUTE" }
687 }, protectValues
[] = {
688 { 'a', "CSMSG_PROTECT_ALL" },
689 { 'e', "CSMSG_PROTECT_EQUAL" },
690 { 'l', "CSMSG_PROTECT_LOWER" },
691 { 'n', "CSMSG_PROTECT_NONE" }
693 { 'd', "CSMSG_TOYS_DISABLED" },
694 { 'n', "CSMSG_TOYS_PRIVATE" },
695 { 'p', "CSMSG_TOYS_PUBLIC" }
696 }, topicRefreshValues
[] = {
697 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
698 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
699 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
700 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
701 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
702 }, ctcpReactionValues
[] = {
703 { 'n', "CSMSG_CTCPREACTION_NONE" },
704 { 'k', "CSMSG_CTCPREACTION_KICK" },
705 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
706 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
707 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
708 }, banTimeoutValues
[] = {
709 { '0', "CSMSG_BANTIMEOUT_NONE" },
710 { '1', "CSMSG_BANTIMEOUT_10M" },
711 { '2', "CSMSG_BANTIMEOUT_2H" },
712 { '3', "CSMSG_BANTIMEOUT_4H" },
713 { '4', "CSMSG_BANTIMEOUT_1D" },
714 { '5', "CSMSG_BANTIMEOUT_1W" }
717 { 'n', "CSMSG_RESYNC_NEVER" },
718 { '1', "CSMSG_RESYNC_3_HOURS" },
719 { '2', "CSMSG_RESYNC_6_HOURS" },
720 { '3', "CSMSG_RESYNC_12_HOURS" },
721 { '4', "CSMSG_RESYNC_24_HOURS" }
724 static const struct {
728 unsigned int old_idx
;
730 struct charOptionValues
*values
;
732 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
733 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
734 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
735 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
736 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
737 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
738 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
741 struct userData
*helperList
;
742 struct chanData
*channelList
;
743 static struct module *chanserv_module
;
744 static unsigned int userCount
;
745 unsigned int chanserv_read_version
= 0; /* db version control */
747 #define CHANSERV_DB_VERSION 2
749 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
750 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
753 user_level_from_name(const char *name
, unsigned short clamp_level
)
755 unsigned int level
= 0, ii
;
757 level
= strtoul(name
, NULL
, 10);
758 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
759 if(!irccasecmp(name
, accessLevels
[ii
].name
))
760 level
= accessLevels
[ii
].level
;
761 if(level
> clamp_level
)
767 user_level_name_from_level(int level
)
775 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
776 if(level
>= accessLevels
[ii
].level
)
777 highest
= accessLevels
[ii
].title
;
783 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
786 *minl
= strtoul(arg
, &sep
, 10);
794 *maxl
= strtoul(sep
+1, &sep
, 10);
802 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
804 struct userData
*uData
, **head
;
806 if(!channel
|| !handle
)
809 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
810 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
812 for(uData
= helperList
;
813 uData
&& uData
->handle
!= handle
;
814 uData
= uData
->next
);
818 uData
= calloc(1, sizeof(struct userData
));
819 uData
->handle
= handle
;
821 uData
->access
= UL_HELPER
;
827 uData
->next
= helperList
;
829 helperList
->prev
= uData
;
837 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
838 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
841 head
= &(channel
->users
);
844 if(uData
&& (uData
!= *head
))
846 /* Shuffle the user to the head of whatever list he was in. */
848 uData
->next
->prev
= uData
->prev
;
850 uData
->prev
->next
= uData
->next
;
856 (**head
).prev
= uData
;
863 /* Returns non-zero if user has at least the minimum access.
864 * exempt_owner is set when handling !set, so the owner can set things
867 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
869 struct userData
*uData
;
870 struct chanData
*cData
= channel
->channel_info
;
871 unsigned short minimum
= cData
->lvlOpts
[opt
];
874 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
877 if(minimum
<= uData
->access
)
879 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
884 /* Scan for other users authenticated to the same handle
885 still in the channel. If so, keep them listed as present.
887 user is optional, if not null, it skips checking that userNode
888 (for the handle_part function) */
890 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
894 if(IsSuspended(uData
->channel
)
895 || IsUserSuspended(uData
)
896 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
908 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
910 unsigned int eflags
, argc
;
912 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
914 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
915 if(!channel
->channel_info
916 || IsSuspended(channel
->channel_info
)
918 || !ircncasecmp(text
, "ACTION ", 7))
920 /* We dont punish people we know -Rubin
921 * * Figure out the minimum level needed to CTCP the channel *
923 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
926 /* If they are a user of the channel, they are exempt */
927 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
929 /* We need to enforce against them; do so. */
932 argv
[1] = user
->nick
;
934 if(GetUserMode(channel
, user
))
935 eflags
|= ACTION_KICK
;
936 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
937 default: case 'n': return;
939 eflags
|= ACTION_KICK
;
942 eflags
|= ACTION_BAN
;
945 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
946 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
949 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
950 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
953 argv
[argc
++] = bad_ctcp_reason
;
954 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
958 chanserv_create_note_type(const char *name
)
960 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
961 strcpy(ntype
->name
, name
);
963 dict_insert(note_types
, ntype
->name
, ntype
);
968 chanserv_deref_note_type(void *data
)
970 struct note_type
*ntype
= data
;
972 if(--ntype
->refs
> 0)
978 chanserv_flush_note_type(struct note_type
*ntype
)
980 struct chanData
*cData
;
981 for(cData
= channelList
; cData
; cData
= cData
->next
)
982 dict_remove(cData
->notes
, ntype
->name
);
986 chanserv_truncate_notes(struct note_type
*ntype
)
988 struct chanData
*cData
;
990 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
992 for(cData
= channelList
; cData
; cData
= cData
->next
) {
993 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
996 if(strlen(note
->note
) <= ntype
->max_length
)
998 dict_remove2(cData
->notes
, ntype
->name
, 1);
999 note
= realloc(note
, size
);
1000 note
->note
[ntype
->max_length
] = 0;
1001 dict_insert(cData
->notes
, ntype
->name
, note
);
1005 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1007 static struct note
*
1008 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1011 unsigned int len
= strlen(text
);
1013 if(len
> type
->max_length
) len
= type
->max_length
;
1014 note
= calloc(1, sizeof(*note
) + len
);
1016 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1017 memcpy(note
->note
, text
, len
);
1018 note
->note
[len
] = 0;
1019 dict_insert(channel
->notes
, type
->name
, note
);
1025 chanserv_free_note(void *data
)
1027 struct note
*note
= data
;
1029 chanserv_deref_note_type(note
->type
);
1030 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1034 static MODCMD_FUNC(cmd_createnote
) {
1035 struct note_type
*ntype
;
1036 unsigned int arg
= 1, existed
= 0, max_length
;
1038 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1041 ntype
= chanserv_create_note_type(argv
[arg
]);
1042 if(!irccasecmp(argv
[++arg
], "privileged"))
1045 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1046 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1048 else if(!irccasecmp(argv
[arg
], "channel"))
1050 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1053 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1056 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1057 ntype
->set_access
.min_ulevel
= ulvl
;
1059 else if(!irccasecmp(argv
[arg
], "setter"))
1061 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1065 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1069 if(!irccasecmp(argv
[++arg
], "privileged"))
1070 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1071 else if(!irccasecmp(argv
[arg
], "channel_users"))
1072 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1073 else if(!irccasecmp(argv
[arg
], "all"))
1074 ntype
->visible_type
= NOTE_VIS_ALL
;
1076 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1080 if((arg
+1) >= argc
) {
1081 reply("MSG_MISSING_PARAMS", argv
[0]);
1084 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1085 if(max_length
< 20 || max_length
> 450)
1087 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1090 if(existed
&& (max_length
< ntype
->max_length
))
1092 ntype
->max_length
= max_length
;
1093 chanserv_truncate_notes(ntype
);
1095 ntype
->max_length
= max_length
;
1098 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1100 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1105 dict_remove(note_types
, ntype
->name
);
1109 static MODCMD_FUNC(cmd_removenote
) {
1110 struct note_type
*ntype
;
1113 ntype
= dict_find(note_types
, argv
[1], NULL
);
1114 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1117 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1124 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1127 chanserv_flush_note_type(ntype
);
1129 dict_remove(note_types
, argv
[1]);
1130 reply("CSMSG_NOTE_DELETED", argv
[1]);
1135 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1139 if(orig
->modes_set
& change
->modes_clear
)
1141 if(orig
->modes_clear
& change
->modes_set
)
1143 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1144 && strcmp(orig
->new_key
, change
->new_key
))
1146 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1147 && (orig
->new_limit
!= change
->new_limit
))
1152 static char max_length_text
[MAXLEN
+1][16];
1154 static struct helpfile_expansion
1155 chanserv_expand_variable(const char *variable
)
1157 struct helpfile_expansion exp
;
1159 if(!irccasecmp(variable
, "notes"))
1162 exp
.type
= HF_TABLE
;
1163 exp
.value
.table
.length
= 1;
1164 exp
.value
.table
.width
= 3;
1165 exp
.value
.table
.flags
= 0;
1166 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1167 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1168 exp
.value
.table
.contents
[0][0] = "Note Type";
1169 exp
.value
.table
.contents
[0][1] = "Visibility";
1170 exp
.value
.table
.contents
[0][2] = "Max Length";
1171 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1173 struct note_type
*ntype
= iter_data(it
);
1176 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1177 row
= exp
.value
.table
.length
++;
1178 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1179 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1180 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1181 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1183 if(!max_length_text
[ntype
->max_length
][0])
1184 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1185 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1190 exp
.type
= HF_STRING
;
1191 exp
.value
.str
= NULL
;
1195 static struct chanData
*
1196 register_channel(struct chanNode
*cNode
, char *registrar
)
1198 struct chanData
*channel
;
1199 enum levelOption lvlOpt
;
1200 enum charOption chOpt
;
1202 channel
= calloc(1, sizeof(struct chanData
));
1204 channel
->notes
= dict_new();
1205 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1207 channel
->registrar
= strdup(registrar
);
1208 channel
->registered
= now
;
1209 channel
->visited
= now
;
1210 channel
->limitAdjusted
= now
;
1211 channel
->ownerTransfer
= now
;
1212 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1213 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1214 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1215 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1216 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1218 channel
->prev
= NULL
;
1219 channel
->next
= channelList
;
1222 channelList
->prev
= channel
;
1223 channelList
= channel
;
1224 registered_channels
++;
1226 channel
->channel
= cNode
;
1228 cNode
->channel_info
= channel
;
1233 static struct userData
*
1234 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1236 struct userData
*ud
;
1238 if(access
> UL_OWNER
)
1241 ud
= calloc(1, sizeof(*ud
));
1242 ud
->channel
= channel
;
1243 ud
->handle
= handle
;
1245 ud
->access
= access
;
1246 ud
->info
= info
? strdup(info
) : NULL
;
1249 ud
->next
= channel
->users
;
1251 channel
->users
->prev
= ud
;
1252 channel
->users
= ud
;
1254 channel
->userCount
++;
1258 ud
->u_next
= ud
->handle
->channels
;
1260 ud
->u_next
->u_prev
= ud
;
1261 ud
->handle
->channels
= ud
;
1263 ud
->flags
= USER_FLAGS_DEFAULT
;
1267 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1270 del_channel_user(struct userData
*user
, int do_gc
)
1272 struct chanData
*channel
= user
->channel
;
1274 channel
->userCount
--;
1278 user
->prev
->next
= user
->next
;
1280 channel
->users
= user
->next
;
1282 user
->next
->prev
= user
->prev
;
1285 user
->u_prev
->u_next
= user
->u_next
;
1287 user
->handle
->channels
= user
->u_next
;
1289 user
->u_next
->u_prev
= user
->u_prev
;
1293 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1294 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1295 unregister_channel(channel
, "lost all users.");
1299 static struct adduserPending
*
1300 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1302 struct adduserPending
*ap
;
1303 ap
= calloc(1,sizeof(struct adduserPending
));
1304 ap
->channel
= channel
;
1307 ap
->created
= time(NULL
);
1309 /* ap->prev defaults to NULL already.. */
1310 ap
->next
= adduser_pendings
;
1311 if(adduser_pendings
)
1312 adduser_pendings
->prev
= ap
;
1313 adduser_pendings
= ap
;
1314 adduser_pendings_count
++;
1319 del_adduser_pending(struct adduserPending
*ap
)
1322 ap
->prev
->next
= ap
->next
;
1324 adduser_pendings
= ap
->next
;
1327 ap
->next
->prev
= ap
->prev
;
1331 static void expire_adduser_pending();
1333 /* find_adduser_pending(channel, user) will find an arbitrary record
1334 * from user, channel, or user and channel.
1335 * if user or channel are NULL, they will match any records.
1337 static struct adduserPending
*
1338 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1340 struct adduserPending
*ap
;
1342 expire_adduser_pending(); /* why not here.. */
1344 if(!channel
&& !user
) /* 2 nulls matches all */
1345 return(adduser_pendings
);
1346 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1348 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1355 /* Remove all pendings for a user or channel
1357 * called in nickserv.c DelUser() and proto-* unregister_channel()
1360 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1362 struct adduserPending
*ap
;
1364 /* So this is a bit wastefull, i hate dealing with linked lists.
1365 * if its a problem we'll rewrite it right */
1366 while((ap
= find_adduser_pending(channel
, user
))) {
1367 del_adduser_pending(ap
);
1371 /* Called from nickserv.c cmd_auth after someone auths */
1373 process_adduser_pending(struct userNode
*user
)
1375 struct adduserPending
*ap
;
1376 if(!user
->handle_info
)
1377 return; /* not associated with an account */
1378 while((ap
= find_adduser_pending(NULL
, user
)))
1380 struct userData
*actee
;
1381 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1383 /* Already on the userlist. do nothing*/
1387 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1388 scan_user_presence(actee
, NULL
);
1390 del_adduser_pending(ap
);
1395 expire_adduser_pending()
1397 struct adduserPending
*ap
, *ap_next
;
1398 ap
= adduser_pendings
;
1401 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1403 ap_next
= ap
->next
; /* save next */
1404 del_adduser_pending(ap
); /* free and relink */
1405 ap
= ap_next
; /* advance */
1412 static void expire_ban(void *data
);
1415 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1418 unsigned int ii
, l1
, l2
;
1423 bd
= malloc(sizeof(struct banData
));
1425 bd
->channel
= channel
;
1427 bd
->triggered
= triggered
;
1428 bd
->expires
= expires
;
1430 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1432 extern const char *hidden_host_suffix
;
1433 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1437 l2
= strlen(old_name
);
1440 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1442 new_mask
= alloca(MAXLEN
);
1443 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1446 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1448 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1449 bd
->reason
= strdup(reason
);
1452 timeq_add(expires
, expire_ban
, bd
);
1455 bd
->next
= channel
->bans
; /* lamers */
1457 channel
->bans
->prev
= bd
;
1459 channel
->banCount
++;
1466 del_channel_ban(struct banData
*ban
)
1468 ban
->channel
->banCount
--;
1472 ban
->prev
->next
= ban
->next
;
1474 ban
->channel
->bans
= ban
->next
;
1477 ban
->next
->prev
= ban
->prev
;
1480 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1489 expire_ban(void *data
) /* lamer.. */
1491 struct banData
*bd
= data
;
1492 if(!IsSuspended(bd
->channel
))
1494 struct banList bans
;
1495 struct mod_chanmode change
;
1497 bans
= bd
->channel
->channel
->banlist
;
1498 mod_chanmode_init(&change
);
1499 for(ii
=0; ii
<bans
.used
; ii
++)
1501 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1504 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1505 change
.args
[0].u
.hostmask
= bd
->mask
;
1506 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1512 del_channel_ban(bd
);
1515 static void chanserv_expire_suspension(void *data
);
1518 unregister_channel(struct chanData
*channel
, const char *reason
)
1520 struct mod_chanmode change
;
1521 char msgbuf
[MAXLEN
];
1523 /* After channel unregistration, the following must be cleaned
1525 - Channel information.
1527 - Channel bans. (lamers)
1528 - Channel suspension data.
1529 - adduser_pending data.
1530 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1536 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1540 mod_chanmode_init(&change
);
1541 change
.modes_clear
|= MODE_REGISTERED
;
1542 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1545 wipe_adduser_pending(channel
->channel
, NULL
);
1547 while(channel
->users
)
1548 del_channel_user(channel
->users
, 0);
1550 while(channel
->bans
)
1551 del_channel_ban(channel
->bans
);
1553 free(channel
->topic
);
1554 free(channel
->registrar
);
1555 free(channel
->greeting
);
1556 free(channel
->user_greeting
);
1557 free(channel
->topic_mask
);
1560 channel
->prev
->next
= channel
->next
;
1562 channelList
= channel
->next
;
1565 channel
->next
->prev
= channel
->prev
;
1567 if(channel
->suspended
)
1569 struct chanNode
*cNode
= channel
->channel
;
1570 struct suspended
*suspended
, *next_suspended
;
1572 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1574 next_suspended
= suspended
->previous
;
1575 free(suspended
->suspender
);
1576 free(suspended
->reason
);
1577 if(suspended
->expires
)
1578 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1583 cNode
->channel_info
= NULL
;
1585 channel
->channel
->channel_info
= NULL
;
1587 dict_delete(channel
->notes
);
1588 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1589 if(!IsSuspended(channel
))
1590 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1591 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1592 UnlockChannel(channel
->channel
);
1594 registered_channels
--;
1598 expire_channels(UNUSED_ARG(void *data
))
1600 struct chanData
*channel
, *next
;
1601 struct userData
*user
;
1602 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1604 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1605 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1607 for(channel
= channelList
; channel
; channel
= next
)
1609 next
= channel
->next
;
1611 /* See if the channel can be expired. */
1612 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1613 || IsProtected(channel
))
1616 /* Make sure there are no high-ranking users still in the channel. */
1617 for(user
=channel
->users
; user
; user
=user
->next
)
1618 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1623 /* Unregister the channel */
1624 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1625 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1626 unregister_channel(channel
, "registration expired.");
1629 if(chanserv_conf
.channel_expire_frequency
)
1630 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1634 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1636 char protect
= channel
->chOpts
[chProtect
];
1637 struct userData
*cs_victim
, *cs_aggressor
;
1639 /* Don't protect if no one is to be protected, someone is attacking
1640 himself, or if the aggressor is an IRC Operator. */
1641 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1644 /* Don't protect if the victim isn't authenticated (because they
1645 can't be a channel user), unless we are to protect non-users
1647 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1648 if(protect
!= 'a' && !cs_victim
)
1651 /* Protect if the aggressor isn't a user because at this point,
1652 the aggressor can only be less than or equal to the victim. */
1653 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1657 /* If the aggressor was a user, then the victim can't be helped. */
1664 if(cs_victim
->access
> cs_aggressor
->access
)
1669 if(cs_victim
->access
>= cs_aggressor
->access
)
1678 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1680 struct chanData
*cData
= channel
->channel_info
;
1681 struct userData
*cs_victim
;
1683 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1684 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1685 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1687 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1695 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1697 struct chanData
*cData
= channel
->channel_info
;
1698 struct userData
*cs_victim
;
1700 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1701 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1702 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1704 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1713 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1715 if(IsService(victim
))
1717 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1721 if(protect_user(victim
, user
, channel
->channel_info
))
1723 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1731 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1733 if(IsService(victim
))
1735 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1739 if(protect_user(victim
, user
, channel
->channel_info
))
1741 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1748 static struct do_not_register
*
1749 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1751 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1752 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1753 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1754 strcpy(dnr
->reason
, reason
);
1756 if(dnr
->chan_name
[0] == '*')
1757 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1758 else if(strpbrk(dnr
->chan_name
, "*?"))
1759 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1761 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1765 static struct dnrList
1766 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1768 struct dnrList list
;
1770 struct do_not_register
*dnr
;
1772 dnrList_init(&list
);
1773 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1774 dnrList_append(&list
, dnr
);
1775 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1776 dnrList_append(&list
, dnr
);
1778 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1779 if(match_ircglob(chan_name
, iter_key(it
)))
1780 dnrList_append(&list
, iter_data(it
));
1785 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1787 struct dnrList list
;
1788 struct do_not_register
*dnr
;
1790 char buf
[INTERVALLEN
];
1792 list
= chanserv_find_dnrs(chan_name
, handle
);
1793 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1795 dnr
= list
.list
[ii
];
1798 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1799 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1802 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1805 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1810 struct do_not_register
*
1811 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1813 struct do_not_register
*dnr
;
1816 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1820 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1822 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1823 if(match_ircglob(chan_name
, iter_key(it
)))
1824 return iter_data(it
);
1829 static CHANSERV_FUNC(cmd_noregister
)
1832 struct do_not_register
*dnr
;
1833 char buf
[INTERVALLEN
];
1834 unsigned int matches
;
1840 reply("CSMSG_DNR_SEARCH_RESULTS");
1841 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1844 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1846 dnr
= iter_data(it
);
1848 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1850 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1853 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1855 dnr
= iter_data(it
);
1857 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1859 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1862 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1864 dnr
= iter_data(it
);
1866 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1868 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1873 reply("MSG_MATCH_COUNT", matches
);
1875 reply("MSG_NO_MATCHES");
1881 if(!IsChannelName(target
) && (*target
!= '*'))
1883 reply("CSMSG_NOT_DNR", target
);
1889 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1890 if((*target
== '*') && !get_handle_info(target
+ 1))
1892 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1895 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1896 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1900 reply("CSMSG_DNR_SEARCH_RESULTS");
1901 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1904 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1906 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1908 reply("MSG_NO_MATCHES");
1912 static CHANSERV_FUNC(cmd_allowregister
)
1914 const char *chan_name
= argv
[1];
1916 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1918 dict_remove(handle_dnrs
, chan_name
+1);
1919 reply("CSMSG_DNR_REMOVED", chan_name
);
1921 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1923 dict_remove(plain_dnrs
, chan_name
);
1924 reply("CSMSG_DNR_REMOVED", chan_name
);
1926 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1928 dict_remove(mask_dnrs
, chan_name
);
1929 reply("CSMSG_DNR_REMOVED", chan_name
);
1933 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1940 chanserv_get_owned_count(struct handle_info
*hi
)
1942 struct userData
*cList
;
1945 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1946 if(cList
->access
== UL_OWNER
)
1951 static CHANSERV_FUNC(cmd_register
)
1953 struct handle_info
*handle
;
1954 struct chanData
*cData
;
1955 struct modeNode
*mn
;
1956 char reason
[MAXLEN
];
1958 unsigned int new_channel
, force
=0;
1959 struct do_not_register
*dnr
;
1965 if(channel
->channel_info
)
1967 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1971 if(channel
->bad_channel
)
1973 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1977 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1979 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1984 chan_name
= channel
->name
;
1990 reply("MSG_MISSING_PARAMS", cmd
->name
);
1991 svccmd_send_help_brief(user
, chanserv
, cmd
);
1994 if(!IsChannelName(argv
[1]))
1996 reply("MSG_NOT_CHANNEL_NAME");
2000 if(opserv_bad_channel(argv
[1]))
2002 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2007 chan_name
= argv
[1];
2010 if(argc
>= (new_channel
+2))
2012 if(!IsHelping(user
))
2014 reply("CSMSG_PROXY_FORBIDDEN");
2018 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2020 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2021 dnr
= chanserv_is_dnr(chan_name
, handle
);
2023 /* Check if they are over the limit.. */
2024 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2026 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2033 handle
= user
->handle_info
;
2034 dnr
= chanserv_is_dnr(chan_name
, handle
);
2035 /* Check if they are over the limit.. */
2036 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2038 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2041 /* Check if another service is in the channel */
2043 for(n
= 0; n
< channel
->members
.used
; n
++)
2045 mn
= channel
->members
.list
[n
];
2046 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2048 reply("CSMSG_ANOTHER_SERVICE");
2055 if(!IsHelping(user
))
2056 reply("CSMSG_DNR_CHANNEL", chan_name
);
2058 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2062 /* now handled above for message specilization *
2063 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2065 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2071 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2073 cData
= register_channel(channel
, user
->handle_info
->handle
);
2074 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2075 cData
->modes
= chanserv_conf
.default_modes
;
2077 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2078 if (IsOffChannel(cData
))
2080 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2084 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2085 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2086 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2088 mod_chanmode_announce(chanserv
, channel
, change
);
2089 mod_chanmode_free(change
);
2092 /* Initialize the channel's max user record. */
2093 cData
->max
= channel
->members
.used
;
2095 if(handle
!= user
->handle_info
)
2096 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2098 reply("CSMSG_REG_SUCCESS", channel
->name
);
2100 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2101 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2106 make_confirmation_string(struct userData
*uData
)
2108 static char strbuf
[16];
2113 for(src
= uData
->handle
->handle
; *src
; )
2114 accum
= accum
* 31 + toupper(*src
++);
2116 for(src
= uData
->channel
->channel
->name
; *src
; )
2117 accum
= accum
* 31 + toupper(*src
++);
2118 sprintf(strbuf
, "%08x", accum
);
2122 static CHANSERV_FUNC(cmd_unregister
)
2125 char reason
[MAXLEN
];
2126 struct chanData
*cData
;
2127 struct userData
*uData
;
2129 cData
= channel
->channel_info
;
2132 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2136 uData
= GetChannelUser(cData
, user
->handle_info
);
2137 if(!uData
|| (uData
->access
< UL_OWNER
))
2139 reply("CSMSG_NO_ACCESS");
2143 if(IsProtected(cData
))
2145 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2149 if(!IsHelping(user
))
2151 const char *confirm_string
;
2152 if(IsSuspended(cData
))
2154 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2157 confirm_string
= make_confirmation_string(uData
);
2158 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2160 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2165 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2166 name
= strdup(channel
->name
);
2167 unregister_channel(cData
, reason
);
2168 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2169 reply("CSMSG_UNREG_SUCCESS", name
);
2175 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2177 extern struct userNode
*spamserv
;
2178 struct mod_chanmode
*change
;
2180 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2182 change
= mod_chanmode_alloc(2);
2184 change
->args
[0].mode
= MODE_CHANOP
;
2185 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2186 change
->args
[1].mode
= MODE_CHANOP
;
2187 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2191 change
= mod_chanmode_alloc(1);
2193 change
->args
[0].mode
= MODE_CHANOP
;
2194 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2197 mod_chanmode_announce(chanserv
, channel
, change
);
2198 mod_chanmode_free(change
);
2201 static CHANSERV_FUNC(cmd_move
)
2203 struct mod_chanmode change
;
2204 struct chanNode
*target
;
2205 struct modeNode
*mn
;
2206 struct userData
*uData
;
2207 char reason
[MAXLEN
];
2208 struct do_not_register
*dnr
;
2209 int chanserv_join
= 0, spamserv_join
;
2213 if(IsProtected(channel
->channel_info
))
2215 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2219 if(!IsChannelName(argv
[1]))
2221 reply("MSG_NOT_CHANNEL_NAME");
2225 if(opserv_bad_channel(argv
[1]))
2227 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2231 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2233 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2235 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2237 if(!IsHelping(user
))
2238 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2240 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2246 mod_chanmode_init(&change
);
2247 if(!(target
= GetChannel(argv
[1])))
2249 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2250 if(!IsSuspended(channel
->channel_info
))
2253 else if(target
->channel_info
)
2255 reply("CSMSG_ALREADY_REGGED", target
->name
);
2258 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2259 && !IsHelping(user
))
2261 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2264 else if(!IsSuspended(channel
->channel_info
))
2269 /* Clear MODE_REGISTERED from old channel, add it to new. */
2271 change
.modes_clear
= MODE_REGISTERED
;
2272 mod_chanmode_announce(chanserv
, channel
, &change
);
2273 change
.modes_clear
= 0;
2274 change
.modes_set
= MODE_REGISTERED
;
2275 mod_chanmode_announce(chanserv
, target
, &change
);
2278 /* Move the channel_info to the target channel; it
2279 shouldn't be necessary to clear timeq callbacks
2280 for the old channel. */
2281 target
->channel_info
= channel
->channel_info
;
2282 target
->channel_info
->channel
= target
;
2283 channel
->channel_info
= NULL
;
2285 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2288 ss_cs_join_channel(target
, spamserv_join
);
2290 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2291 if(!IsSuspended(target
->channel_info
))
2293 char reason2
[MAXLEN
];
2294 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2295 DelChannelUser(chanserv
, channel
, reason2
, 0);
2297 UnlockChannel(channel
);
2298 LockChannel(target
);
2299 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2300 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2305 merge_users(struct chanData
*source
, struct chanData
*target
)
2307 struct userData
*suData
, *tuData
, *next
;
2313 /* Insert the source's users into the scratch area. */
2314 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2315 dict_insert(merge
, suData
->handle
->handle
, suData
);
2317 /* Iterate through the target's users, looking for
2318 users common to both channels. The lower access is
2319 removed from either the scratch area or target user
2321 for(tuData
= target
->users
; tuData
; tuData
= next
)
2323 struct userData
*choice
;
2325 next
= tuData
->next
;
2327 /* If a source user exists with the same handle as a target
2328 channel's user, resolve the conflict by removing one. */
2329 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2333 /* Pick the data we want to keep. */
2334 /* If the access is the same, use the later seen time. */
2335 if(suData
->access
== tuData
->access
)
2336 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2337 else /* Otherwise, keep the higher access level. */
2338 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2340 /* Remove the user that wasn't picked. */
2341 if(choice
== tuData
)
2343 dict_remove(merge
, suData
->handle
->handle
);
2344 del_channel_user(suData
, 0);
2347 del_channel_user(tuData
, 0);
2350 /* Move the remaining users to the target channel. */
2351 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2353 suData
= iter_data(it
);
2355 /* Insert the user into the target channel's linked list. */
2356 suData
->prev
= NULL
;
2357 suData
->next
= target
->users
;
2358 suData
->channel
= target
;
2361 target
->users
->prev
= suData
;
2362 target
->users
= suData
;
2364 /* Update the user counts for the target channel; the
2365 source counts are left alone. */
2366 target
->userCount
++;
2369 /* Possible to assert (source->users == NULL) here. */
2370 source
->users
= NULL
;
2375 merge_bans(struct chanData
*source
, struct chanData
*target
)
2377 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2379 /* Hold on to the original head of the target ban list
2380 to avoid comparing source bans with source bans. */
2381 tFront
= target
->bans
;
2383 /* Perform a totally expensive O(n*m) merge, ick. */
2384 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2386 /* Flag to track whether the ban's been moved
2387 to the destination yet. */
2390 /* Possible to assert (sbData->prev == NULL) here. */
2391 sNext
= sbData
->next
;
2393 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2395 tNext
= tbData
->next
;
2397 /* Perform two comparisons between each source
2398 and target ban, conflicts are resolved by
2399 keeping the broader ban and copying the later
2400 expiration and triggered time. */
2401 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2403 /* There is a broader ban in the target channel that
2404 overrides one in the source channel; remove the
2405 source ban and break. */
2406 if(sbData
->expires
> tbData
->expires
)
2407 tbData
->expires
= sbData
->expires
;
2408 if(sbData
->triggered
> tbData
->triggered
)
2409 tbData
->triggered
= sbData
->triggered
;
2410 del_channel_ban(sbData
);
2413 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2415 /* There is a broader ban in the source channel that
2416 overrides one in the target channel; remove the
2417 target ban, fall through and move the source over. */
2418 if(tbData
->expires
> sbData
->expires
)
2419 sbData
->expires
= tbData
->expires
;
2420 if(tbData
->triggered
> sbData
->triggered
)
2421 sbData
->triggered
= tbData
->triggered
;
2422 if(tbData
== tFront
)
2424 del_channel_ban(tbData
);
2427 /* Source bans can override multiple target bans, so
2428 we allow a source to run through this loop multiple
2429 times, but we can only move it once. */
2434 /* Remove the source ban from the source ban list. */
2436 sbData
->next
->prev
= sbData
->prev
;
2438 /* Modify the source ban's associated channel. */
2439 sbData
->channel
= target
;
2441 /* Insert the ban into the target channel's linked list. */
2442 sbData
->prev
= NULL
;
2443 sbData
->next
= target
->bans
;
2446 target
->bans
->prev
= sbData
;
2447 target
->bans
= sbData
;
2449 /* Update the user counts for the target channel. */
2454 /* Possible to assert (source->bans == NULL) here. */
2455 source
->bans
= NULL
;
2459 merge_data(struct chanData
*source
, struct chanData
*target
)
2461 /* Use more recent visited and owner-transfer time; use older
2462 * registered time. Bitwise or may_opchan. Use higher max.
2463 * Do not touch last_refresh, ban count or user counts.
2465 if(source
->visited
> target
->visited
)
2466 target
->visited
= source
->visited
;
2467 if(source
->registered
< target
->registered
)
2468 target
->registered
= source
->registered
;
2469 if(source
->ownerTransfer
> target
->ownerTransfer
)
2470 target
->ownerTransfer
= source
->ownerTransfer
;
2471 if(source
->may_opchan
)
2472 target
->may_opchan
= 1;
2473 if(source
->max
> target
->max
)
2474 target
->max
= source
->max
;
2478 merge_channel(struct chanData
*source
, struct chanData
*target
)
2480 merge_users(source
, target
);
2481 merge_bans(source
, target
);
2482 merge_data(source
, target
);
2485 static CHANSERV_FUNC(cmd_merge
)
2487 struct userData
*target_user
;
2488 struct chanNode
*target
;
2489 char reason
[MAXLEN
];
2493 /* Make sure the target channel exists and is registered to the user
2494 performing the command. */
2495 if(!(target
= GetChannel(argv
[1])))
2497 reply("MSG_INVALID_CHANNEL");
2501 if(!target
->channel_info
)
2503 reply("CSMSG_NOT_REGISTERED", target
->name
);
2507 if(IsProtected(channel
->channel_info
))
2509 reply("CSMSG_MERGE_NODELETE");
2513 if(IsSuspended(target
->channel_info
))
2515 reply("CSMSG_MERGE_SUSPENDED");
2519 if(channel
== target
)
2521 reply("CSMSG_MERGE_SELF");
2525 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2526 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2528 reply("CSMSG_MERGE_NOT_OWNER");
2532 /* Merge the channel structures and associated data. */
2533 merge_channel(channel
->channel_info
, target
->channel_info
);
2534 spamserv_cs_move_merge(user
, channel
, target
, 0);
2535 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2536 unregister_channel(channel
->channel_info
, reason
);
2537 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2541 static CHANSERV_FUNC(cmd_opchan
)
2543 struct mod_chanmode change
;
2544 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2546 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2549 channel
->channel_info
->may_opchan
= 0;
2550 mod_chanmode_init(&change
);
2552 change
.args
[0].mode
= MODE_CHANOP
;
2553 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2554 mod_chanmode_announce(chanserv
, channel
, &change
);
2555 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2559 static CHANSERV_FUNC(cmd_adduser
)
2561 struct userData
*actee
;
2562 struct userData
*actor
;
2563 struct handle_info
*handle
;
2564 unsigned short access
;
2568 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2570 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2574 access
= user_level_from_name(argv
[2], UL_OWNER
);
2577 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2581 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2582 if(actor
->access
<= access
)
2584 reply("CSMSG_NO_BUMP_ACCESS");
2588 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2590 // 'kevin must first authenticate with AuthServ.' is sent to user
2591 struct userNode
*unode
;
2592 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2595 if(find_adduser_pending(channel
, unode
)) {
2596 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2599 if(IsInChannel(channel
, unode
)) {
2600 reply("CSMSG_ADDUSER_PENDING");
2601 add_adduser_pending(channel
, unode
, access
);
2602 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2604 /* this results in user must auth AND not in chan errors. too confusing..
2606 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2614 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2616 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2620 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2621 scan_user_presence(actee
, NULL
);
2622 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2626 static CHANSERV_FUNC(cmd_clvl
)
2628 struct handle_info
*handle
;
2629 struct userData
*victim
;
2630 struct userData
*actor
;
2631 unsigned short new_access
;
2632 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2636 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2638 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2641 if(handle
== user
->handle_info
&& !privileged
)
2643 reply("CSMSG_NO_SELF_CLVL");
2647 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2649 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2653 if(actor
->access
<= victim
->access
&& !privileged
)
2655 reply("MSG_USER_OUTRANKED", handle
->handle
);
2659 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2663 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2667 if(new_access
>= actor
->access
&& !privileged
)
2669 reply("CSMSG_NO_BUMP_ACCESS");
2673 victim
->access
= new_access
;
2674 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2678 static CHANSERV_FUNC(cmd_deluser
)
2680 struct handle_info
*handle
;
2681 struct userData
*victim
;
2682 struct userData
*actor
;
2683 unsigned short access
;
2688 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2690 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2693 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2695 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2701 access
= user_level_from_name(argv
[1], UL_OWNER
);
2704 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2707 if(access
!= victim
->access
)
2709 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2715 access
= victim
->access
;
2718 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2720 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2724 chan_name
= strdup(channel
->name
);
2725 del_channel_user(victim
, 1);
2726 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2732 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2734 struct userData
*actor
, *uData
, *next
;
2736 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2738 if(min_access
> max_access
)
2740 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2744 if((actor
->access
<= max_access
) && !IsHelping(user
))
2746 reply("CSMSG_NO_ACCESS");
2750 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2754 if((uData
->access
>= min_access
)
2755 && (uData
->access
<= max_access
)
2756 && match_ircglob(uData
->handle
->handle
, mask
))
2757 del_channel_user(uData
, 1);
2760 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2764 static CHANSERV_FUNC(cmd_mdelowner
)
2766 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2769 static CHANSERV_FUNC(cmd_mdelcoowner
)
2771 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2774 static CHANSERV_FUNC(cmd_mdelmanager
)
2776 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2779 static CHANSERV_FUNC(cmd_mdelop
)
2781 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2784 static CHANSERV_FUNC(cmd_mdelpeon
)
2786 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2789 static CHANSERV_FUNC(cmd_mdelhalfop
)
2791 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2797 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2799 struct banData
*bData
, *next
;
2800 char interval
[INTERVALLEN
];
2805 limit
= now
- duration
;
2806 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2810 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2813 del_channel_ban(bData
);
2817 intervalString(interval
, duration
, user
->handle_info
);
2818 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2823 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2825 struct userData
*actor
, *uData
, *next
;
2826 char interval
[INTERVALLEN
];
2830 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2831 if(min_access
> max_access
)
2833 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2837 if((actor
->access
<= max_access
) && !IsHelping(user
))
2839 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2844 limit
= now
- duration
;
2845 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2849 if((uData
->seen
> limit
)
2851 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2854 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2855 || (!max_access
&& (uData
->access
< actor
->access
)))
2857 del_channel_user(uData
, 1);
2865 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2867 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2871 static CHANSERV_FUNC(cmd_trim
)
2873 unsigned long duration
;
2874 unsigned short min_level
, max_level
;
2879 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2880 duration
= ParseInterval(argv
[2]);
2883 reply("CSMSG_CANNOT_TRIM");
2887 if(!irccasecmp(argv
[1], "lamers"))
2889 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2892 else if(!irccasecmp(argv
[1], "users"))
2894 cmd_trim_users(user
, channel
, 0, 0, duration
, vacation
);
2897 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2899 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
, vacation
);
2902 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2904 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
, vacation
);
2909 reply("CSMSG_INVALID_TRIM", argv
[1]);
2914 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2915 to the user. cmd_all takes advantage of this. */
2916 static CHANSERV_FUNC(cmd_up
)
2918 struct mod_chanmode change
;
2919 struct userData
*uData
;
2922 mod_chanmode_init(&change
);
2924 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2925 if(!change
.args
[0].u
.member
)
2928 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2932 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2936 reply("CSMSG_GODMODE_UP", argv
[0]);
2939 else if(uData
->access
>= UL_OP
)
2941 change
.args
[0].mode
= MODE_CHANOP
;
2942 errmsg
= "CSMSG_ALREADY_OPPED";
2944 else if(uData
->access
>= UL_HALFOP
)
2946 change
.args
[0].mode
= MODE_HALFOP
;
2947 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2949 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2951 change
.args
[0].mode
= MODE_VOICE
;
2952 errmsg
= "CSMSG_ALREADY_VOICED";
2957 reply("CSMSG_NO_ACCESS");
2960 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2961 if(!change
.args
[0].mode
)
2964 reply(errmsg
, channel
->name
);
2967 modcmd_chanmode_announce(&change
);
2971 static CHANSERV_FUNC(cmd_down
)
2973 struct mod_chanmode change
;
2975 mod_chanmode_init(&change
);
2977 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2978 if(!change
.args
[0].u
.member
)
2981 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2985 if(!change
.args
[0].u
.member
->modes
)
2988 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2992 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2993 modcmd_chanmode_announce(&change
);
2997 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
)
2999 struct userData
*cList
;
3001 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3003 if(IsSuspended(cList
->channel
)
3004 || IsUserSuspended(cList
)
3005 || !GetUserMode(cList
->channel
->channel
, user
))
3008 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3014 static CHANSERV_FUNC(cmd_upall
)
3016 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3019 static CHANSERV_FUNC(cmd_downall
)
3021 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3024 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3025 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3028 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
)
3030 unsigned int ii
, valid
;
3031 struct userNode
*victim
;
3032 struct mod_chanmode
*change
;
3034 change
= mod_chanmode_alloc(argc
- 1);
3036 for(ii
=valid
=0; ++ii
< argc
; )
3038 if(!(victim
= GetUserH(argv
[ii
])))
3040 change
->args
[valid
].mode
= mode
;
3041 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3042 if(!change
->args
[valid
].u
.member
)
3044 if(validate
&& !validate(user
, channel
, victim
))
3049 change
->argc
= valid
;
3050 if(valid
< (argc
-1))
3051 reply("CSMSG_PROCESS_FAILED");
3054 modcmd_chanmode_announce(change
);
3055 reply(action
, channel
->name
);
3057 mod_chanmode_free(change
);
3061 static CHANSERV_FUNC(cmd_op
)
3063 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3066 static CHANSERV_FUNC(cmd_hop
)
3068 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3071 static CHANSERV_FUNC(cmd_deop
)
3073 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3076 static CHANSERV_FUNC(cmd_dehop
)
3078 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3081 static CHANSERV_FUNC(cmd_voice
)
3083 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3086 static CHANSERV_FUNC(cmd_devoice
)
3088 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3092 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3098 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3100 struct modeNode
*mn
= channel
->members
.list
[ii
];
3102 if(IsService(mn
->user
))
3105 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3108 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3112 victims
[(*victimCount
)++] = mn
;
3118 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3120 struct userNode
*victim
;
3121 struct modeNode
**victims
;
3122 unsigned int offset
, n
, victimCount
, duration
= 0;
3123 char *reason
= "Bye.", *ban
, *name
;
3124 char interval
[INTERVALLEN
];
3126 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3127 REQUIRE_PARAMS(offset
);
3130 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3131 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3133 /* Truncate the reason to a length of TOPICLEN, as
3134 the ircd does; however, leave room for an ellipsis
3135 and the kicker's nick. */
3136 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3140 if((victim
= GetUserH(argv
[1])))
3142 victims
= alloca(sizeof(victims
[0]));
3143 victims
[0] = GetUserMode(channel
, victim
);
3144 /* XXX: The comparison with ACTION_KICK is just because all
3145 * other actions can work on users outside the channel, and we
3146 * want to allow those (e.g. unbans) in that case. If we add
3147 * some other ejection action for in-channel users, change
3149 victimCount
= victims
[0] ? 1 : 0;
3151 if(IsService(victim
))
3154 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3158 if((action
== ACTION_KICK
) && !victimCount
)
3161 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3165 if(protect_user(victim
, user
, channel
->channel_info
))
3167 // This translates to send_message(user, cmd->parent->bot, ...)
3168 // if user is x3 (ctcp action) cmd is null and segfault.
3170 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3174 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3175 name
= victim
->nick
;
3179 if(!is_ircmask(argv
[1]))
3182 reply("MSG_NICK_UNKNOWN", argv
[1]);
3186 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3188 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3191 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3194 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3195 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3197 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3198 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3199 some creativity, but its not x3's job to be the ban censor anyway. */
3200 if(is_overmask(argv
[1]))
3203 reply("CSMSG_LAME_MASK", argv
[1]);
3207 if((action
== ACTION_KICK
) && (victimCount
== 0))
3210 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3214 name
= ban
= strdup(argv
[1]);
3217 /* Truncate the ban in place if necessary; we must ensure
3218 that 'ban' is a valid ban mask before sanitizing it. */
3219 sanitize_ircmask(ban
);
3221 if(action
& ACTION_ADD_LAMER
)
3223 struct banData
*bData
, *next
;
3225 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3228 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3233 if(action
& ACTION_ADD_TIMED_LAMER
)
3235 duration
= ParseInterval(argv
[2]);
3240 reply("CSMSG_DURATION_TOO_LOW");
3244 else if(duration
> (86400 * 365 * 2))
3247 reply("CSMSG_DURATION_TOO_HIGH");
3254 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3256 if(match_ircglobs(bData
->mask
, ban
))
3258 int exact
= !irccasecmp(bData
->mask
, ban
);
3260 /* The ban is redundant; there is already a ban
3261 with the same effect in place. */
3265 free(bData
->reason
);
3266 bData
->reason
= strdup(reason
);
3267 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3269 reply("CSMSG_REASON_CHANGE", ban
);
3273 if(exact
&& bData
->expires
)
3277 /* If the ban matches an existing one exactly,
3278 extend the expiration time if the provided
3279 duration is longer. */
3280 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3282 bData
->expires
= now
+ duration
;
3293 /* Delete the expiration timeq entry and
3294 requeue if necessary. */
3295 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3298 timeq_add(bData
->expires
, expire_ban
, bData
);
3302 /* automated kickban, dont reply */
3305 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3307 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3313 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3320 if(match_ircglobs(ban
, bData
->mask
))
3322 /* The ban we are adding makes previously existing
3323 bans redundant; silently remove them. */
3324 del_channel_ban(bData
);
3328 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
);
3330 name
= ban
= strdup(bData
->mask
);
3334 /* WHAT DOES THIS DO?? -Rubin */
3335 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3337 extern const char *hidden_host_suffix
;
3338 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3340 unsigned int l1
, l2
;
3343 l2
= strlen(old_name
);
3346 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3348 new_mask
= malloc(MAXLEN
);
3349 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3351 name
= ban
= new_mask
;
3356 if(action
& ACTION_BAN
)
3358 unsigned int exists
;
3359 struct mod_chanmode
*change
;
3361 if(channel
->banlist
.used
>= MAXBANS
)
3364 reply("CSMSG_BANLIST_FULL", channel
->name
);
3369 exists
= ChannelBanExists(channel
, ban
);
3370 change
= mod_chanmode_alloc(victimCount
+ 1);
3371 for(n
= 0; n
< victimCount
; ++n
)
3373 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3374 change
->args
[n
].u
.member
= victims
[n
];
3378 change
->args
[n
].mode
= MODE_BAN
;
3379 change
->args
[n
++].u
.hostmask
= ban
;
3383 modcmd_chanmode_announce(change
);
3385 mod_chanmode_announce(chanserv
, channel
, change
);
3386 mod_chanmode_free(change
);
3388 if(exists
&& (action
== ACTION_BAN
))
3391 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3397 if(action
& ACTION_KICK
)
3399 char kick_reason
[MAXLEN
];
3400 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3402 for(n
= 0; n
< victimCount
; n
++)
3403 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3408 /* No response, since it was automated. */
3410 else if(action
& ACTION_ADD_LAMER
)
3413 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3415 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3417 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3418 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3419 else if(action
& ACTION_BAN
)
3420 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3421 else if(action
& ACTION_KICK
&& victimCount
)
3422 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3428 static CHANSERV_FUNC(cmd_kickban
)
3430 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3433 static CHANSERV_FUNC(cmd_kick
)
3435 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3438 static CHANSERV_FUNC(cmd_ban
)
3440 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3443 static CHANSERV_FUNC(cmd_addlamer
)
3445 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3448 static CHANSERV_FUNC(cmd_addtimedlamer
)
3450 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3453 static struct mod_chanmode
*
3454 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3456 struct mod_chanmode
*change
;
3457 unsigned char *match
;
3458 unsigned int ii
, count
;
3460 match
= alloca(bans
->used
);
3463 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3465 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3466 MATCH_USENICK
| MATCH_VISIBLE
);
3473 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3475 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3482 change
= mod_chanmode_alloc(count
);
3483 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3487 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3488 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3490 assert(count
== change
->argc
);
3494 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3496 unsigned int jj
, ii
, count
;
3498 struct chanData
*channel
;
3500 struct mod_chanmode
*change
;
3502 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3503 /* Walk through every channel */
3504 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3505 switch(channel
->chOpts
[chBanTimeout
])
3507 default: case '0': continue; /* Dont remove bans in this chan */
3508 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3509 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3510 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3511 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3512 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3515 /* First find out how many bans were going to unset */
3516 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3517 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3521 /* At least one ban, so setup a removal */
3522 change
= mod_chanmode_alloc(count
);
3524 /* Walk over every ban in this channel.. */
3525 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3526 bn
= channel
->channel
->banlist
.list
[jj
];
3527 if (bn
->set
< bantimeout
) {
3528 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3530 /* Add this ban to the mode change */
3531 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3532 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3534 /* Pull this ban out of the list */
3535 banList_remove(&(channel
->channel
->banlist
), bn
);
3540 /* Send the modes to IRC */
3541 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3543 /* free memory from strdup above */
3544 for(ii
= 0; ii
< count
; ++ii
)
3545 free((char*)change
->args
[ii
].u
.hostmask
);
3547 mod_chanmode_free(change
);
3550 /* Set this function to run again */
3551 if(chanserv_conf
.ban_timeout_frequency
)
3552 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3557 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3559 struct userNode
*actee
;
3565 /* may want to allow a comma delimited list of users... */
3566 if(!(actee
= GetUserH(argv
[1])))
3568 if(!is_ircmask(argv
[1]))
3570 reply("MSG_NICK_UNKNOWN", argv
[1]);
3574 mask
= strdup(argv
[1]);
3577 /* We don't sanitize the mask here because ircu
3579 if(action
& ACTION_UNBAN
)
3581 struct mod_chanmode
*change
;
3582 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3587 modcmd_chanmode_announce(change
);
3588 for(ii
= 0; ii
< change
->argc
; ++ii
)
3589 free((char*)change
->args
[ii
].u
.hostmask
);
3590 mod_chanmode_free(change
);
3595 if(action
& ACTION_DEL_LAMER
)
3597 struct banData
*ban
, *next
;
3599 ban
= channel
->channel_info
->bans
; /* lamers */
3603 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3606 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3611 del_channel_ban(ban
);
3618 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3620 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3626 static CHANSERV_FUNC(cmd_unban
)
3628 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3631 static CHANSERV_FUNC(cmd_dellamer
)
3633 /* it doesn't necessarily have to remove the channel ban - may want
3634 to make that an option. */
3635 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3638 static CHANSERV_FUNC(cmd_unbanme
)
3640 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3641 long flags
= ACTION_UNBAN
;
3643 /* remove permanent bans if the user has the proper access. */
3644 if(uData
->access
>= UL_MANAGER
)
3645 flags
|= ACTION_DEL_LAMER
;
3647 argv
[1] = user
->nick
;
3648 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3651 static CHANSERV_FUNC(cmd_unbanall
)
3653 struct mod_chanmode
*change
;
3656 if(!channel
->banlist
.used
)
3658 reply("CSMSG_NO_BANS", channel
->name
);
3662 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3663 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3665 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3666 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3668 modcmd_chanmode_announce(change
);
3669 for(ii
= 0; ii
< change
->argc
; ++ii
)
3670 free((char*)change
->args
[ii
].u
.hostmask
);
3671 mod_chanmode_free(change
);
3672 reply("CSMSG_BANS_REMOVED", channel
->name
);
3676 static CHANSERV_FUNC(cmd_open
)
3678 struct mod_chanmode
*change
;
3681 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3683 change
= mod_chanmode_alloc(0);
3684 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3685 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3686 && channel
->channel_info
->modes
.modes_set
)
3687 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3688 modcmd_chanmode_announce(change
);
3689 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3690 for(ii
= 0; ii
< change
->argc
; ++ii
)
3691 free((char*)change
->args
[ii
].u
.hostmask
);
3692 mod_chanmode_free(change
);
3696 static CHANSERV_FUNC(cmd_myaccess
)
3698 static struct string_buffer sbuf
;
3699 struct handle_info
*target_handle
;
3700 struct userData
*uData
;
3703 target_handle
= user
->handle_info
;
3704 else if(!IsHelping(user
))
3706 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3709 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3712 if(!target_handle
->channels
)
3714 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3718 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3719 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3721 struct chanData
*cData
= uData
->channel
;
3723 if(uData
->access
> UL_OWNER
)
3725 if(IsProtected(cData
)
3726 && (target_handle
!= user
->handle_info
)
3727 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3730 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3731 if(uData
->flags
== USER_AUTO_OP
)
3732 string_buffer_append(&sbuf
, ',');
3733 if(IsUserSuspended(uData
))
3734 string_buffer_append(&sbuf
, 's');
3735 if(IsUserAutoOp(uData
))
3737 if(uData
->access
>= UL_OP
)
3738 string_buffer_append(&sbuf
, 'o');
3739 else if(uData
->access
>= UL_HALFOP
)
3740 string_buffer_append(&sbuf
, 'h');
3741 else if(uData
->access
>= UL_PEON
)
3742 string_buffer_append(&sbuf
, 'v');
3744 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3745 string_buffer_append(&sbuf
, 'i');
3747 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3749 string_buffer_append_string(&sbuf
, ")]");
3750 string_buffer_append(&sbuf
, '\0');
3751 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3757 static CHANSERV_FUNC(cmd_access
)
3759 struct userNode
*target
;
3760 struct handle_info
*target_handle
;
3761 struct userData
*uData
;
3763 char prefix
[MAXLEN
];
3768 target_handle
= target
->handle_info
;
3770 else if((target
= GetUserH(argv
[1])))
3772 target_handle
= target
->handle_info
;
3774 else if(argv
[1][0] == '*')
3776 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3778 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3784 reply("MSG_NICK_UNKNOWN", argv
[1]);
3788 assert(target
|| target_handle
);
3790 if(target
== chanserv
)
3792 reply("CSMSG_IS_CHANSERV");
3800 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3805 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3808 reply("MSG_AUTHENTICATE");
3814 const char *epithet
= NULL
, *type
= NULL
;
3817 epithet
= chanserv_conf
.irc_operator_epithet
;
3820 else if(IsNetworkHelper(target
))
3822 epithet
= chanserv_conf
.network_helper_epithet
;
3823 type
= "network helper";
3825 else if(IsSupportHelper(target
))
3827 epithet
= chanserv_conf
.support_helper_epithet
;
3828 type
= "support helper";
3832 if(target_handle
->epithet
)
3833 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3835 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3837 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3841 sprintf(prefix
, "%s", target_handle
->handle
);
3844 if(!channel
->channel_info
)
3846 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3850 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3851 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3852 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3854 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3855 /* To prevent possible information leaks, only show infolines
3856 * if the requestor is in the channel or it's their own
3858 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3860 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3862 /* Likewise, only say it's suspended if the user has active
3863 * access in that channel or it's their own entry. */
3864 if(IsUserSuspended(uData
)
3865 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3866 || (user
->handle_info
== uData
->handle
)))
3868 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3873 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3879 /* This is never used...
3881 zoot_list(struct listData *list)
3883 struct userData *uData;
3884 unsigned int start, curr, highest, lowest;
3885 struct helpfile_table tmp_table;
3886 const char **temp, *msg;
3888 if(list->table.length == 1)
3891 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);
3893 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));
3894 msg = user_find_message(list->user, "MSG_NONE");
3895 send_message_type(4, list->user, list->bot, " %s", msg);
3897 tmp_table.width = list->table.width;
3898 tmp_table.flags = list->table.flags;
3899 list->table.contents[0][0] = " ";
3900 highest = list->highest;
3901 if(list->lowest != 0)
3902 lowest = list->lowest;
3903 else if(highest < 100)
3906 lowest = highest - 100;
3907 for(start = curr = 1; curr < list->table.length; )
3909 uData = list->users[curr-1];
3910 list->table.contents[curr++][0] = " ";
3911 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3914 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);
3916 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));
3917 temp = list->table.contents[--start];
3918 list->table.contents[start] = list->table.contents[0];
3919 tmp_table.contents = list->table.contents + start;
3920 tmp_table.length = curr - start;
3921 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3922 list->table.contents[start] = temp;
3924 highest = lowest - 1;
3925 lowest = (highest < 100) ? 0 : (highest - 99);
3932 normal_list(struct listData
*list
)
3936 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
);
3938 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
));
3939 if(list
->table
.length
== 1)
3941 msg
= user_find_message(list
->user
, "MSG_NONE");
3942 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3945 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3948 /* if these need changed, uncomment and customize
3950 clean_list(struct listData *list)
3954 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);
3956 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));
3957 if(list->table.length == 1)
3959 msg = user_find_message(list->user, "MSG_NONE");
3960 send_message_type(4, list->user, list->bot, " %s", msg);
3963 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3967 advanced_list(struct listData *list)
3971 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);
3973 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));
3974 if(list->table.length == 1)
3976 msg = user_find_message(list->user, "MSG_NONE");
3977 send_message_type(4, list->user, list->bot, " %s", msg);
3980 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3984 classic_list(struct listData *list)
3988 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
3990 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
3991 if(list->table.length == 1)
3993 msg = user_find_message(list->user, "MSG_NONE");
3994 send_message_type(4, list->user, list->bot, " %s", msg);
3997 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4002 userData_access_comp(const void *arg_a
, const void *arg_b
)
4004 const struct userData
*a
= *(struct userData
**)arg_a
;
4005 const struct userData
*b
= *(struct userData
**)arg_b
;
4007 if(a
->access
!= b
->access
)
4008 res
= b
->access
- a
->access
;
4010 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4015 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4017 void (*send_list
)(struct listData
*);
4018 struct userData
*uData
;
4019 struct listData lData
;
4020 unsigned int matches
;
4026 lData
.bot
= cmd
->parent
->bot
;
4027 lData
.channel
= channel
;
4028 lData
.lowest
= lowest
;
4029 lData
.highest
= highest
;
4030 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4031 send_list
= normal_list
;
4032 /* What does the following line do exactly?? */
4033 /*(void)zoot_list; ** since it doesn't show user levels */
4036 if(user->handle_info)
4038 switch(user->handle_info->userlist_style)
4040 case HI_STYLE_CLEAN:
4041 send_list = clean_list;
4043 case HI_STYLE_ADVANCED:
4044 send_list = advanced_list;
4046 case HI_STYLE_CLASSIC:
4047 send_list = classic_list;
4049 case HI_STYLE_NORMAL:
4051 send_list = normal_list;
4056 send_list
= normal_list
;
4058 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4060 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4062 if((uData
->access
< lowest
)
4063 || (uData
->access
> highest
)
4064 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4066 lData
.users
[matches
++] = uData
;
4068 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4070 lData
.table
.length
= matches
+1;
4071 lData
.table
.flags
= TABLE_NO_FREE
;
4072 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4074 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4075 lData
.table
.width
= 5; /* with level = 5 */
4077 lData
.table
.width
= 4; /* without = 4 */
4078 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4079 lData
.table
.contents
[0] = ary
;
4080 if(user
->handle_info
) {
4081 switch(user
->handle_info
->userlist_style
) {
4082 case HI_STYLE_CLASSIC
:
4085 case HI_STYLE_ADVANCED
:
4086 ary
[i
++] = "Access";
4089 case HI_STYLE_CLEAN
:
4090 ary
[i
++] = "Access";
4092 case HI_STYLE_NORMAL
:
4094 ary
[i
++] = "Access";
4099 ary
[i
++] = "Access";
4101 ary
[i
++] = "Account";
4102 ary
[i
] = "Last Seen";
4104 ary
[i
++] = "Status";
4105 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4107 struct userData
*uData
= lData
.users
[matches
-1];
4108 char seen
[INTERVALLEN
];
4111 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4112 lData
.table
.contents
[matches
] = ary
;
4113 if(user
->handle_info
) {
4114 switch(user
->handle_info
->userlist_style
) {
4115 case HI_STYLE_CLASSIC
:
4116 ary
[i
++] = strtab(uData
->access
);
4118 case HI_STYLE_ADVANCED
:
4119 ary
[i
++] = user_level_name_from_level(uData
->access
);
4120 ary
[i
++] = strtab(uData
->access
);
4122 case HI_STYLE_CLEAN
:
4123 ary
[i
++] = user_level_name_from_level(uData
->access
);
4125 case HI_STYLE_NORMAL
:
4127 ary
[i
++] = user_level_name_from_level(uData
->access
);
4132 ary
[i
++] = user_level_name_from_level(uData
->access
);
4134 ary
[i
++] = uData
->handle
->handle
;
4137 else if(!uData
->seen
)
4140 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4141 ary
[i
] = strdup(ary
[i
]);
4143 if(IsUserSuspended(uData
))
4144 ary
[i
++] = "Suspended";
4145 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4146 ary
[i
++] = "Vacation";
4148 ary
[i
++] = "Normal";
4151 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4153 /* Free strdup above */
4154 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4155 free(lData
.table
.contents
[matches
]);
4157 free(lData
.table
.contents
[0]);
4158 free(lData
.table
.contents
);
4162 /* Remove this now that debugging is over? or improve it for
4163 * users? Would it be better tied into USERS somehow? -Rubin */
4164 static CHANSERV_FUNC(cmd_pending
)
4166 struct adduserPending
*ap
;
4167 reply("CSMSG_ADDUSER_PENDING_HEADER");
4168 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4170 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4171 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4172 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4176 static CHANSERV_FUNC(cmd_users
)
4178 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4181 static CHANSERV_FUNC(cmd_wlist
)
4183 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4186 static CHANSERV_FUNC(cmd_clist
)
4188 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4191 static CHANSERV_FUNC(cmd_mlist
)
4193 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4196 static CHANSERV_FUNC(cmd_olist
)
4198 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4201 static CHANSERV_FUNC(cmd_hlist
)
4203 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4206 static CHANSERV_FUNC(cmd_plist
)
4208 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4211 static CHANSERV_FUNC(cmd_lamers
)
4213 struct helpfile_table tbl
;
4214 unsigned int matches
= 0, timed
= 0, ii
;
4215 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4216 const char *msg_never
, *triggered
, *expires
;
4217 struct banData
*ban
, **bans
; /* lamers */
4224 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4225 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4228 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4230 if(search
&& !match_ircglobs(search
, ban
->mask
))
4232 bans
[matches
++] = ban
;
4237 tbl
.length
= matches
+ 1;
4238 tbl
.width
= 4 + timed
;
4240 tbl
.flags
= TABLE_NO_FREE
;
4241 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4242 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4243 tbl
.contents
[0][0] = "Mask";
4244 tbl
.contents
[0][1] = "Set By";
4245 tbl
.contents
[0][2] = "Triggered";
4248 tbl
.contents
[0][3] = "Expires";
4249 tbl
.contents
[0][4] = "Reason";
4252 tbl
.contents
[0][3] = "Reason";
4255 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4257 free(tbl
.contents
[0]);
4262 msg_never
= user_find_message(user
, "MSG_NEVER");
4263 for(ii
= 0; ii
< matches
; )
4269 else if(ban
->expires
)
4270 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4272 expires
= msg_never
;
4275 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4277 triggered
= msg_never
;
4279 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4280 tbl
.contents
[ii
][0] = ban
->mask
;
4281 tbl
.contents
[ii
][1] = ban
->owner
;
4282 tbl
.contents
[ii
][2] = strdup(triggered
);
4285 tbl
.contents
[ii
][3] = strdup(expires
);
4286 tbl
.contents
[ii
][4] = ban
->reason
;
4289 tbl
.contents
[ii
][3] = ban
->reason
;
4291 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4292 /* reply("MSG_MATCH_COUNT", matches); */
4293 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4295 free((char*)tbl
.contents
[ii
][2]);
4297 free((char*)tbl
.contents
[ii
][3]);
4298 free(tbl
.contents
[ii
]);
4300 free(tbl
.contents
[0]);
4307 * return + if the user does NOT have the right to set the topic, and
4308 * the topic is changed.
4311 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4313 struct chanData
*cData
= channel
->channel_info
;
4314 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4316 else if(cData
->topic
)
4317 return irccasecmp(new_topic
, cData
->topic
);
4324 * Makes a givin topic fit into a givin topic mask and returns
4327 * topic_mask - the mask to conform to
4328 * topic - the topic to make conform
4329 * new_topic - the pre-allocated char* to put the new topic into
4331 * modifies: new_topic
4334 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4336 //char *topic_mask = cData->topic_mask;
4338 int pos
=0, starpos
=-1, dpos
=0, len
;
4340 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4347 strcpy(new_topic
, "");
4350 len
= strlen(topic
);
4351 if((dpos
+ len
) > TOPICLEN
)
4352 len
= TOPICLEN
+ 1 - dpos
;
4353 memcpy(new_topic
+dpos
, topic
, len
);
4357 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4358 default: new_topic
[dpos
++] = tchar
; break;
4361 if((dpos
> TOPICLEN
) || tchar
)
4363 strcpy(new_topic
, "");
4366 new_topic
[dpos
] = 0;
4370 static CHANSERV_FUNC(cmd_topic
)
4372 struct chanData
*cData
;
4376 #ifdef WITH_PROTOCOL_P10
4380 cData
= channel
->channel_info
;
4385 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4386 reply("CSMSG_TOPIC_SET", cData
->topic
);
4390 reply("CSMSG_NO_TOPIC", channel
->name
);
4394 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4395 /* If they say "!topic *", use an empty topic. */
4396 if((topic
[0] == '*') && (topic
[1] == 0))
4399 if(bad_topic(channel
, user
, topic
))
4401 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4406 /* If there is a topicmask set, and the new topic doesnt match, make it */
4407 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4409 char *topic_mask
= cData
->topic_mask
;
4410 char new_topic
[TOPICLEN
+1];
4412 /* make a new topic fitting mask */
4413 conform_topic(topic_mask
, topic
, new_topic
);
4416 /* Topic couldnt fit into mask, was too long */
4417 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4418 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4421 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4423 else /* No mask set, just set the topic */
4424 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4427 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4429 /* Grab the topic and save it as the default topic. */
4431 cData
->topic
= strdup(channel
->topic
);
4437 static CHANSERV_FUNC(cmd_mode
)
4439 struct userData
*uData
;
4440 struct mod_chanmode
*change
;
4445 change
= &channel
->channel_info
->modes
;
4446 if(change
->modes_set
|| change
->modes_clear
) {
4447 modcmd_chanmode_announce(change
);
4448 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4450 reply("CSMSG_NO_MODES", channel
->name
);
4454 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4456 base_oplevel
= MAXOPLEVEL
;
4457 else if (uData
->access
>= UL_OWNER
)
4460 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4461 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4465 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4469 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4470 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4473 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4474 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4478 modcmd_chanmode_announce(change
);
4479 mod_chanmode_free(change
);
4480 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4484 static CHANSERV_FUNC(cmd_invite
)
4486 struct userData
*uData
;
4487 struct userNode
*invite
;
4489 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4493 if(!(invite
= GetUserH(argv
[1])))
4495 reply("MSG_NICK_UNKNOWN", argv
[1]);
4502 if(GetUserMode(channel
, invite
))
4504 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4512 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4513 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4516 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4518 irc_invite(chanserv
, invite
, channel
);
4520 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4525 static CHANSERV_FUNC(cmd_inviteme
)
4527 if(GetUserMode(channel
, user
))
4529 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4532 if(channel
->channel_info
4533 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4535 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4538 irc_invite(cmd
->parent
->bot
, user
, channel
);
4543 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4546 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4548 /* We display things based on two dimensions:
4549 * - Issue time: present or absent
4550 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4551 * (in order of precedence, so something both expired and revoked
4552 * only counts as revoked)
4554 combo
= (suspended
->issued
? 4 : 0)
4555 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4557 case 0: /* no issue time, indefinite expiration */
4558 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4560 case 1: /* no issue time, expires in future */
4561 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4562 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4564 case 2: /* no issue time, expired */
4565 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4566 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4568 case 3: /* no issue time, revoked */
4569 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4570 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4572 case 4: /* issue time set, indefinite expiration */
4573 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4574 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4576 case 5: /* issue time set, expires in future */
4577 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4578 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4579 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4581 case 6: /* issue time set, expired */
4582 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4583 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4584 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4586 case 7: /* issue time set, revoked */
4587 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4588 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4589 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4592 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4598 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4601 const char *fmt
= "%a %b %d %H:%M %Y";
4602 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4604 if(giveownership
->staff_issuer
)
4606 if(giveownership
->reason
)
4607 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4608 giveownership
->target
, giveownership
->target_access
,
4609 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4611 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4612 giveownership
->target
, giveownership
->target_access
,
4613 giveownership
->staff_issuer
, buf
);
4617 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4622 static CHANSERV_FUNC(cmd_info
)
4624 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4625 struct userData
*uData
, *owner
;
4626 struct chanData
*cData
;
4627 struct do_not_register
*dnr
;
4632 cData
= channel
->channel_info
;
4633 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4634 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4637 uData
= GetChannelUser(cData
, user
->handle_info
);
4638 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4640 mod_chanmode_format(&cData
->modes
, modes
);
4641 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4642 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4645 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4649 note
= iter_data(it
);
4650 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4653 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4654 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4657 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4658 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4659 if(owner
->access
== UL_OWNER
)
4660 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4661 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4662 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4663 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4665 privileged
= IsStaff(user
);
4667 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4668 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4669 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4671 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4672 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4674 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4676 struct suspended
*suspended
;
4677 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4678 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4679 show_suspension_info(cmd
, user
, suspended
);
4681 else if(IsSuspended(cData
))
4683 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4684 show_suspension_info(cmd
, user
, cData
->suspended
);
4686 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4688 struct giveownership
*giveownership
;
4689 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4690 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4691 show_giveownership_info(cmd
, user
, giveownership
);
4693 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4694 reply("CSMSG_CHANNEL_END");
4696 reply("CSMSG_CHANNEL_END_CLEAN");
4700 static CHANSERV_FUNC(cmd_netinfo
)
4702 extern time_t boot_time
;
4703 extern unsigned long burst_length
;
4704 char interval
[INTERVALLEN
];
4706 reply("CSMSG_NETWORK_INFO");
4707 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4708 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4709 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4710 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4711 reply("CSMSG_NETWORK_LAMERS", banCount
);
4712 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4713 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4714 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4719 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4721 struct helpfile_table table
;
4723 struct userNode
*user
;
4728 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4729 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4730 for(nn
=0; nn
<list
->used
; nn
++)
4732 user
= list
->list
[nn
];
4733 if(user
->modes
& skip_flags
)
4737 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4740 nick
= alloca(strlen(user
->nick
)+3);
4741 sprintf(nick
, "(%s)", user
->nick
);
4745 table
.contents
[table
.length
][0] = nick
;
4748 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4751 static CHANSERV_FUNC(cmd_ircops
)
4753 reply("CSMSG_STAFF_OPERS");
4754 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4758 static CHANSERV_FUNC(cmd_helpers
)
4760 reply("CSMSG_STAFF_HELPERS");
4761 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4765 static CHANSERV_FUNC(cmd_staff
)
4767 reply("CSMSG_NETWORK_STAFF");
4768 cmd_ircops(CSFUNC_ARGS
);
4769 cmd_helpers(CSFUNC_ARGS
);
4773 static CHANSERV_FUNC(cmd_peek
)
4775 struct modeNode
*mn
;
4776 char modes
[MODELEN
];
4778 struct helpfile_table table
;
4780 irc_make_chanmode(channel
, modes
);
4782 reply("CSMSG_PEEK_INFO", channel
->name
);
4783 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4785 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4786 reply("CSMSG_PEEK_MODES", modes
);
4787 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4791 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4792 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4793 for(n
= 0; n
< channel
->members
.used
; n
++)
4795 mn
= channel
->members
.list
[n
];
4796 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4798 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4799 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4804 reply("CSMSG_PEEK_OPS");
4805 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4808 reply("CSMSG_PEEK_NO_OPS");
4809 reply("CSMSG_PEEK_END");
4813 static MODCMD_FUNC(cmd_wipeinfo
)
4815 struct handle_info
*victim
;
4816 struct userData
*ud
, *actor
;
4819 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4820 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4822 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4824 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4827 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4829 reply("MSG_USER_OUTRANKED", victim
->handle
);
4835 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4840 resync_channel(struct chanNode
*channel
)
4842 struct mod_chanmode
*changes
;
4843 struct chanData
*cData
= channel
->channel_info
;
4844 unsigned int ii
, used
;
4846 /* 6 = worst case -ovh+ovh on everyone */
4847 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4848 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4850 struct modeNode
*mn
= channel
->members
.list
[ii
];
4851 struct userData
*uData
;
4853 if(IsService(mn
->user
))
4857 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4859 /* If the channel is in no-mode mode, de-mode EVERYONE */
4860 if(cData
->chOpts
[chAutomode
] == 'n')
4864 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4865 changes
->args
[used
++].u
.member
= mn
;
4868 else /* Give various userlevels their modes.. */
4870 if(uData
&& uData
->access
>= UL_OP
)
4872 if(!(mn
->modes
& MODE_CHANOP
))
4874 changes
->args
[used
].mode
= MODE_CHANOP
;
4875 changes
->args
[used
++].u
.member
= mn
;
4878 else if(uData
&& uData
->access
>= UL_HALFOP
)
4880 if(mn
->modes
& MODE_CHANOP
)
4882 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4883 changes
->args
[used
++].u
.member
= mn
;
4885 if(!(mn
->modes
& MODE_HALFOP
))
4887 changes
->args
[used
].mode
= MODE_HALFOP
;
4888 changes
->args
[used
++].u
.member
= mn
;
4891 else if(uData
&& uData
->access
>= UL_PEON
)
4893 if(mn
->modes
& MODE_CHANOP
)
4895 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4896 changes
->args
[used
++].u
.member
= mn
;
4898 if(mn
->modes
& MODE_HALFOP
)
4900 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4901 changes
->args
[used
++].u
.member
= mn
;
4903 /* Don't voice peons if were in mode m */
4904 if( cData
->chOpts
[chAutomode
] == 'm')
4906 if(mn
->modes
& MODE_VOICE
)
4908 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4909 changes
->args
[used
++].u
.member
= mn
;
4912 /* otherwise, make user they do have voice */
4913 else if(!(mn
->modes
& MODE_VOICE
))
4915 changes
->args
[used
].mode
= MODE_VOICE
;
4916 changes
->args
[used
++].u
.member
= mn
;
4919 else /* They arnt on the userlist.. */
4921 /* If we voice everyone, but they dont.. */
4922 if(cData
->chOpts
[chAutomode
] == 'v')
4924 /* Remove anything except v */
4925 if(mn
->modes
& ~MODE_VOICE
)
4927 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4928 changes
->args
[used
++].u
.member
= mn
;
4931 if(!(mn
->modes
& MODE_VOICE
))
4933 changes
->args
[used
].mode
= MODE_VOICE
;
4934 changes
->args
[used
++].u
.member
= mn
;
4937 /* If we hop everyone, but they dont.. */
4938 else if(cData
->chOpts
[chAutomode
] == 'h')
4940 /* Remove anything except h */
4941 if(mn
->modes
& ~MODE_HALFOP
)
4943 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4944 changes
->args
[used
++].u
.member
= mn
;
4947 if(!(mn
->modes
& MODE_HALFOP
))
4949 changes
->args
[used
].mode
= MODE_HALFOP
;
4950 changes
->args
[used
++].u
.member
= mn
;
4953 /* If we op everyone, but they dont.. */
4954 else if(cData
->chOpts
[chAutomode
] == 'o')
4956 /* Remove anything except h */
4957 if(mn
->modes
& ~MODE_CHANOP
)
4959 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4960 changes
->args
[used
++].u
.member
= mn
;
4963 if(!(mn
->modes
& MODE_CHANOP
))
4965 changes
->args
[used
].mode
= MODE_CHANOP
;
4966 changes
->args
[used
++].u
.member
= mn
;
4969 /* they have no excuse for having modes, de-everything them */
4974 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4975 changes
->args
[used
++].u
.member
= mn
;
4981 changes
->argc
= used
;
4982 mod_chanmode_announce(chanserv
, channel
, changes
);
4983 mod_chanmode_free(changes
);
4986 static CHANSERV_FUNC(cmd_resync
)
4988 resync_channel(channel
);
4989 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4993 static CHANSERV_FUNC(cmd_seen
)
4995 struct userData
*uData
;
4996 struct handle_info
*handle
;
4997 char seen
[INTERVALLEN
];
5001 if(!irccasecmp(argv
[1], chanserv
->nick
))
5003 reply("CSMSG_IS_CHANSERV");
5007 if(!(handle
= get_handle_info(argv
[1])))
5009 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5013 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5015 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5020 reply("CSMSG_USER_PRESENT", handle
->handle
);
5021 else if(uData
->seen
)
5022 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5024 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5026 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5027 reply("CSMSG_USER_VACATION", handle
->handle
);
5032 static MODCMD_FUNC(cmd_names
)
5034 struct userNode
*targ
;
5035 struct userData
*targData
;
5036 unsigned int ii
, pos
;
5039 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5041 targ
= channel
->members
.list
[ii
]->user
;
5042 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5045 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5048 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5052 if(IsUserSuspended(targData
))
5054 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5057 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5058 reply("CSMSG_END_NAMES", channel
->name
);
5063 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5065 switch(ntype
->visible_type
)
5067 case NOTE_VIS_ALL
: return 1;
5068 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5069 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5074 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5076 struct userData
*uData
;
5078 switch(ntype
->set_access_type
)
5080 case NOTE_SET_CHANNEL_ACCESS
:
5081 if(!user
->handle_info
)
5083 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5085 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5086 case NOTE_SET_CHANNEL_SETTER
:
5087 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5088 case NOTE_SET_PRIVILEGED
: default:
5089 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5093 static CHANSERV_FUNC(cmd_note
)
5095 struct chanData
*cData
;
5097 struct note_type
*ntype
;
5099 cData
= channel
->channel_info
;
5102 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5106 /* If no arguments, show all visible notes for the channel. */
5112 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5114 note
= iter_data(it
);
5115 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5118 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5119 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5122 reply("CSMSG_NOTELIST_END", channel
->name
);
5124 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5126 /* If one argument, show the named note. */
5129 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5130 && note_type_visible_to_user(cData
, note
->type
, user
))
5132 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5134 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5135 && note_type_visible_to_user(NULL
, ntype
, user
))
5137 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5142 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5146 /* Assume they're trying to set a note. */
5150 ntype
= dict_find(note_types
, argv
[1], NULL
);
5153 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5156 else if(note_type_settable_by_user(channel
, ntype
, user
))
5158 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5159 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5160 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5161 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5162 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5164 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5166 /* The note is viewable to staff only, so return 0
5167 to keep the invocation from getting logged (or
5168 regular users can see it in !events). */
5174 reply("CSMSG_NO_ACCESS");
5181 static CHANSERV_FUNC(cmd_delnote
)
5186 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5187 || !note_type_settable_by_user(channel
, note
->type
, user
))
5189 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5192 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5193 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5197 static CHANSERV_FUNC(cmd_last
)
5203 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5205 if(numoflines
< 1 || numoflines
> 200)
5207 reply("CSMSG_LAST_INVALID");
5210 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5214 static CHANSERV_FUNC(cmd_events
)
5216 struct logSearch discrim
;
5217 struct logReport report
;
5218 unsigned int matches
, limit
;
5220 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5221 if(limit
< 1 || limit
> 200)
5224 memset(&discrim
, 0, sizeof(discrim
));
5225 discrim
.masks
.bot
= chanserv
;
5226 discrim
.masks
.channel_name
= channel
->name
;
5228 discrim
.masks
.command
= argv
[2];
5229 discrim
.limit
= limit
;
5230 discrim
.max_time
= INT_MAX
;
5231 discrim
.severities
= 1 << LOG_COMMAND
;
5232 report
.reporter
= chanserv
;
5234 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5235 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5237 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5239 reply("MSG_MATCH_COUNT", matches
);
5241 reply("MSG_NO_MATCHES");
5245 static CHANSERV_FUNC(cmd_say
)
5251 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5252 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5254 else if(GetUserH(argv
[1]))
5257 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5258 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5262 reply("MSG_NOT_TARGET_NAME");
5268 static CHANSERV_FUNC(cmd_emote
)
5274 /* CTCP is so annoying. */
5275 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5276 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5278 else if(GetUserH(argv
[1]))
5280 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5281 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5285 reply("MSG_NOT_TARGET_NAME");
5291 struct channelList
*
5292 chanserv_support_channels(void)
5294 return &chanserv_conf
.support_channels
;
5297 static CHANSERV_FUNC(cmd_expire
)
5299 int channel_count
= registered_channels
;
5300 expire_channels(NULL
);
5301 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5306 chanserv_expire_suspension(void *data
)
5308 struct suspended
*suspended
= data
;
5309 struct chanNode
*channel
;
5311 if(!suspended
->expires
|| (now
< suspended
->expires
))
5312 suspended
->revoked
= now
;
5313 channel
= suspended
->cData
->channel
;
5314 suspended
->cData
->channel
= channel
;
5315 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5316 if(!IsOffChannel(suspended
->cData
))
5318 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5319 ss_cs_join_channel(channel
, 1);
5323 static CHANSERV_FUNC(cmd_csuspend
)
5325 struct suspended
*suspended
;
5326 char reason
[MAXLEN
];
5327 time_t expiry
, duration
;
5328 struct userData
*uData
;
5332 if(IsProtected(channel
->channel_info
))
5334 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5338 if(argv
[1][0] == '!')
5340 else if(IsSuspended(channel
->channel_info
))
5342 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5343 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5347 if(!strcmp(argv
[1], "0"))
5349 else if((duration
= ParseInterval(argv
[1])))
5350 expiry
= now
+ duration
;
5353 reply("MSG_INVALID_DURATION", argv
[1]);
5357 unsplit_string(argv
+ 2, argc
- 2, reason
);
5359 suspended
= calloc(1, sizeof(*suspended
));
5360 suspended
->revoked
= 0;
5361 suspended
->issued
= now
;
5362 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5363 suspended
->expires
= expiry
;
5364 suspended
->reason
= strdup(reason
);
5365 suspended
->cData
= channel
->channel_info
;
5366 suspended
->previous
= suspended
->cData
->suspended
;
5367 suspended
->cData
->suspended
= suspended
;
5369 if(suspended
->expires
)
5370 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5372 if(IsSuspended(channel
->channel_info
))
5374 suspended
->previous
->revoked
= now
;
5375 if(suspended
->previous
->expires
)
5376 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5377 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5378 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5382 /* Mark all users in channel as absent. */
5383 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5392 /* Mark the channel as suspended, then part. */
5393 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5394 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5395 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5396 reply("CSMSG_SUSPENDED", channel
->name
);
5397 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5398 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5403 static CHANSERV_FUNC(cmd_cunsuspend
)
5405 struct suspended
*suspended
;
5406 char message
[MAXLEN
];
5408 if(!IsSuspended(channel
->channel_info
))
5410 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5414 suspended
= channel
->channel_info
->suspended
;
5416 /* Expire the suspension and join ChanServ to the channel. */
5417 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5418 chanserv_expire_suspension(suspended
);
5419 reply("CSMSG_UNSUSPENDED", channel
->name
);
5420 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5421 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5425 typedef struct chanservSearch
5433 unsigned long flags
;
5437 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5440 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5445 search
= malloc(sizeof(struct chanservSearch
));
5446 memset(search
, 0, sizeof(*search
));
5449 for(i
= 0; i
< argc
; i
++)
5451 /* Assume all criteria require arguments. */
5454 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5458 if(!irccasecmp(argv
[i
], "name"))
5459 search
->name
= argv
[++i
];
5460 else if(!irccasecmp(argv
[i
], "registrar"))
5461 search
->registrar
= argv
[++i
];
5462 else if(!irccasecmp(argv
[i
], "unvisited"))
5463 search
->unvisited
= ParseInterval(argv
[++i
]);
5464 else if(!irccasecmp(argv
[i
], "registered"))
5465 search
->registered
= ParseInterval(argv
[++i
]);
5466 else if(!irccasecmp(argv
[i
], "flags"))
5469 if(!irccasecmp(argv
[i
], "nodelete"))
5470 search
->flags
|= CHANNEL_NODELETE
;
5471 else if(!irccasecmp(argv
[i
], "suspended"))
5472 search
->flags
|= CHANNEL_SUSPENDED
;
5475 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5479 else if(!irccasecmp(argv
[i
], "limit"))
5480 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5483 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5488 if(search
->name
&& !strcmp(search
->name
, "*"))
5490 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5491 search
->registrar
= 0;
5500 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5502 const char *name
= channel
->channel
->name
;
5503 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5504 (search
->registrar
&& !channel
->registrar
) ||
5505 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5506 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5507 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5508 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5515 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5517 struct chanData
*channel
;
5518 unsigned int matches
= 0;
5520 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5522 if(!chanserv_channel_match(channel
, search
))
5532 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5537 search_print(struct chanData
*channel
, void *data
)
5539 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5542 static CHANSERV_FUNC(cmd_search
)
5545 unsigned int matches
;
5546 channel_search_func action
;
5550 if(!irccasecmp(argv
[1], "count"))
5551 action
= search_count
;
5552 else if(!irccasecmp(argv
[1], "print"))
5553 action
= search_print
;
5556 reply("CSMSG_ACTION_INVALID", argv
[1]);
5560 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5564 if(action
== search_count
)
5565 search
->limit
= INT_MAX
;
5567 if(action
== search_print
)
5569 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5570 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5574 matches
= chanserv_channel_search(search
, action
, user
);
5577 reply("MSG_MATCH_COUNT", matches
);
5579 reply("MSG_NO_MATCHES");
5585 static CHANSERV_FUNC(cmd_unvisited
)
5587 struct chanData
*cData
;
5588 time_t interval
= chanserv_conf
.channel_expire_delay
;
5589 char buffer
[INTERVALLEN
];
5590 unsigned int limit
= 25, matches
= 0;
5594 interval
= ParseInterval(argv
[1]);
5596 limit
= atoi(argv
[2]);
5599 intervalString(buffer
, interval
, user
->handle_info
);
5600 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5602 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5604 if((now
- cData
->visited
) < interval
)
5607 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5608 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5615 static MODCMD_FUNC(chan_opt_defaulttopic
)
5621 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5623 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5627 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5629 free(channel
->channel_info
->topic
);
5630 if(topic
[0] == '*' && topic
[1] == 0)
5632 topic
= channel
->channel_info
->topic
= NULL
;
5636 topic
= channel
->channel_info
->topic
= strdup(topic
);
5637 if(channel
->channel_info
->topic_mask
5638 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5639 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5641 SetChannelTopic(channel
, chanserv
, chanserv
, topic
? topic
: "", 1);
5644 if(channel
->channel_info
->topic
)
5645 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5647 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5651 static MODCMD_FUNC(chan_opt_topicmask
)
5655 struct chanData
*cData
= channel
->channel_info
;
5658 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5660 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5664 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5666 if(cData
->topic_mask
)
5667 free(cData
->topic_mask
);
5668 if(mask
[0] == '*' && mask
[1] == 0)
5670 cData
->topic_mask
= 0;
5674 cData
->topic_mask
= strdup(mask
);
5676 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5677 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5678 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5682 if(channel
->channel_info
->topic_mask
)
5683 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5685 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5689 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5693 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5697 if(greeting
[0] == '*' && greeting
[1] == 0)
5701 unsigned int length
= strlen(greeting
);
5702 if(length
> chanserv_conf
.greeting_length
)
5704 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5707 *data
= strdup(greeting
);
5716 reply(name
, user_find_message(user
, "MSG_NONE"));
5720 static MODCMD_FUNC(chan_opt_greeting
)
5722 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5725 static MODCMD_FUNC(chan_opt_usergreeting
)
5727 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5730 static MODCMD_FUNC(chan_opt_modes
)
5732 struct mod_chanmode
*new_modes
;
5733 char modes
[MODELEN
];
5737 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5739 reply("CSMSG_NO_ACCESS");
5742 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5744 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5746 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5748 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5751 else if(new_modes
->argc
> 1)
5753 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5754 mod_chanmode_free(new_modes
);
5759 channel
->channel_info
->modes
= *new_modes
;
5760 modcmd_chanmode_announce(new_modes
);
5761 mod_chanmode_free(new_modes
);
5765 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5767 reply("CSMSG_SET_MODES", modes
);
5769 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5773 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5775 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5777 struct chanData
*cData
= channel
->channel_info
;
5782 /* Set flag according to value. */
5783 if(enabled_string(argv
[1]))
5785 cData
->flags
|= mask
;
5788 else if(disabled_string(argv
[1]))
5790 cData
->flags
&= ~mask
;
5795 reply("MSG_INVALID_BINARY", argv
[1]);
5801 /* Find current option value. */
5802 value
= (cData
->flags
& mask
) ? 1 : 0;
5806 reply(name
, user_find_message(user
, "MSG_ON"));
5808 reply(name
, user_find_message(user
, "MSG_OFF"));
5812 static MODCMD_FUNC(chan_opt_nodelete
)
5814 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5816 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5820 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5823 static MODCMD_FUNC(chan_opt_dynlimit
)
5825 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5828 static MODCMD_FUNC(chan_opt_offchannel
)
5830 struct chanData
*cData
= channel
->channel_info
;
5835 /* Set flag according to value. */
5836 if(enabled_string(argv
[1]))
5838 if(!IsOffChannel(cData
))
5839 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5840 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5843 else if(disabled_string(argv
[1]))
5845 if(IsOffChannel(cData
))
5847 struct mod_chanmode change
;
5848 mod_chanmode_init(&change
);
5850 change
.args
[0].mode
= MODE_CHANOP
;
5851 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5852 mod_chanmode_announce(chanserv
, channel
, &change
);
5854 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5859 reply("MSG_INVALID_BINARY", argv
[1]);
5865 /* Find current option value. */
5866 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5870 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5872 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5876 static MODCMD_FUNC(chan_opt_defaults
)
5878 struct userData
*uData
;
5879 struct chanData
*cData
;
5880 const char *confirm
;
5881 enum levelOption lvlOpt
;
5882 enum charOption chOpt
;
5884 cData
= channel
->channel_info
;
5885 uData
= GetChannelUser(cData
, user
->handle_info
);
5886 if(!uData
|| (uData
->access
< UL_OWNER
))
5888 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5891 confirm
= make_confirmation_string(uData
);
5892 if((argc
< 2) || strcmp(argv
[1], confirm
))
5894 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5897 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5898 cData
->modes
= chanserv_conf
.default_modes
;
5899 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5900 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5901 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5902 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5903 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5908 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5910 struct chanData
*cData
= channel
->channel_info
;
5911 struct userData
*uData
;
5912 unsigned short value
;
5916 if(!check_user_level(channel
, user
, option
, 1, 1))
5918 reply("CSMSG_CANNOT_SET");
5921 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5922 if(!value
&& strcmp(argv
[1], "0"))
5924 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5927 uData
= GetChannelUser(cData
, user
->handle_info
);
5928 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5930 reply("CSMSG_BAD_SETLEVEL");
5936 /* This test only applies to owners, since non-owners
5937 * trying to set an option to above their level get caught
5938 * by the CSMSG_BAD_SETLEVEL test above.
5940 if(value
> uData
->access
)
5942 reply("CSMSG_BAD_SETTERS");
5949 cData
->lvlOpts
[option
] = value
;
5951 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5955 static MODCMD_FUNC(chan_opt_enfops
)
5957 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5960 static MODCMD_FUNC(chan_opt_enfhalfops
)
5962 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5964 static MODCMD_FUNC(chan_opt_enfmodes
)
5966 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5969 static MODCMD_FUNC(chan_opt_enftopic
)
5971 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5974 static MODCMD_FUNC(chan_opt_pubcmd
)
5976 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5979 static MODCMD_FUNC(chan_opt_setters
)
5981 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5984 static MODCMD_FUNC(chan_opt_userinfo
)
5986 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5989 static MODCMD_FUNC(chan_opt_topicsnarf
)
5991 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5994 static MODCMD_FUNC(chan_opt_inviteme
)
5996 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5999 /* TODO: Make look like this when no args are
6001 * -X3- -------------------------------
6002 * -X3- BanTimeout: Bans are removed:
6003 * -X3- ----- * indicates current -----
6004 * -X3- 0: [*] Never.
6005 * -X3- 1: [ ] After 10 minutes.
6006 * -X3- 2: [ ] After 2 hours.
6007 * -X3- 3: [ ] After 4 hours.
6008 * -X3- 4: [ ] After 24 hours.
6009 * -X3- 5: [ ] After one week.
6010 * -X3- ------------- End -------------
6013 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6015 struct chanData
*cData
= channel
->channel_info
;
6016 int count
= charOptions
[option
].count
, index
;
6020 index
= atoi(argv
[1]);
6022 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6024 reply("CSMSG_INVALID_NUMERIC", index
);
6025 /* Show possible values. */
6026 for(index
= 0; index
< count
; index
++)
6027 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6031 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6035 /* Find current option value. */
6038 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6042 /* Somehow, the option value is corrupt; reset it to the default. */
6043 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6048 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6052 static MODCMD_FUNC(chan_opt_automode
)
6054 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6057 static MODCMD_FUNC(chan_opt_protect
)
6059 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6062 static MODCMD_FUNC(chan_opt_toys
)
6064 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6067 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6069 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6072 static MODCMD_FUNC(chan_opt_bantimeout
)
6074 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6077 static MODCMD_FUNC(chan_opt_topicrefresh
)
6079 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6082 static MODCMD_FUNC(chan_opt_resync
)
6084 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6087 static struct svccmd_list set_shows_list
;
6090 handle_svccmd_unbind(struct svccmd
*target
) {
6092 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6093 if(target
== set_shows_list
.list
[ii
])
6094 set_shows_list
.used
= 0;
6097 static CHANSERV_FUNC(cmd_set
)
6099 struct svccmd
*subcmd
;
6103 /* Check if we need to (re-)initialize set_shows_list. */
6104 if(!set_shows_list
.used
)
6106 if(!set_shows_list
.size
)
6108 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6109 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6111 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6113 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6114 sprintf(buf
, "%s %s", argv
[0], name
);
6115 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6118 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6121 svccmd_list_append(&set_shows_list
, subcmd
);
6127 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6128 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6130 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6132 subcmd
= set_shows_list
.list
[ii
];
6133 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6135 reply("CSMSG_CHANNEL_OPTIONS_END");
6139 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6140 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6143 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6146 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6148 reply("CSMSG_NO_ACCESS");
6152 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6156 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6158 struct userData
*uData
;
6160 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6163 reply("CSMSG_NOT_USER", channel
->name
);
6169 /* Just show current option value. */
6171 else if(enabled_string(argv
[1]))
6173 uData
->flags
|= mask
;
6175 else if(disabled_string(argv
[1]))
6177 uData
->flags
&= ~mask
;
6181 reply("MSG_INVALID_BINARY", argv
[1]);
6185 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6189 static MODCMD_FUNC(user_opt_autoop
)
6191 struct userData
*uData
;
6193 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6196 reply("CSMSG_NOT_USER", channel
->name
);
6199 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6200 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6202 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6203 /* TODO: add halfops error message? or is the op one generic enough? */
6206 static MODCMD_FUNC(user_opt_autoinvite
)
6208 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6211 static MODCMD_FUNC(user_opt_info
)
6213 struct userData
*uData
;
6216 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6220 /* If they got past the command restrictions (which require access)
6221 * but fail this test, we have some fool with security override on.
6223 reply("CSMSG_NOT_USER", channel
->name
);
6230 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6231 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6233 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6236 bp
= strcspn(infoline
, "\001");
6239 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6244 if(infoline
[0] == '*' && infoline
[1] == 0)
6247 uData
->info
= strdup(infoline
);
6250 reply("CSMSG_USET_INFO", uData
->info
);
6252 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6256 struct svccmd_list uset_shows_list
;
6258 static CHANSERV_FUNC(cmd_uset
)
6260 struct svccmd
*subcmd
;
6264 /* Check if we need to (re-)initialize uset_shows_list. */
6265 if(!uset_shows_list
.used
)
6269 "AutoOp", "AutoInvite", "Info"
6272 if(!uset_shows_list
.size
)
6274 uset_shows_list
.size
= ArrayLength(options
);
6275 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6277 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6279 const char *name
= options
[ii
];
6280 sprintf(buf
, "%s %s", argv
[0], name
);
6281 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6284 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6287 svccmd_list_append(&uset_shows_list
, subcmd
);
6293 /* Do this so options are presented in a consistent order. */
6294 reply("CSMSG_USER_OPTIONS");
6295 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6296 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6300 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6301 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6304 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6308 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6311 static CHANSERV_FUNC(cmd_giveownership
)
6313 struct handle_info
*new_owner_hi
;
6314 struct userData
*new_owner
, *curr_user
;
6315 struct chanData
*cData
= channel
->channel_info
;
6316 struct do_not_register
*dnr
;
6317 struct giveownership
*giveownership
;
6318 unsigned int force
, override
;
6319 unsigned short co_access
, new_owner_old_access
;
6320 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6323 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6324 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6326 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6327 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6328 && (uData
->access
> 500)
6329 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6330 || uData
->access
< 500));
6333 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6335 struct userData
*owner
= NULL
;
6336 for(curr_user
= channel
->channel_info
->users
;
6338 curr_user
= curr_user
->next
)
6340 if(curr_user
->access
!= UL_OWNER
)
6344 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6351 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6353 char delay
[INTERVALLEN
];
6354 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6355 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6358 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6360 if(new_owner_hi
== user
->handle_info
)
6362 reply("CSMSG_NO_TRANSFER_SELF");
6365 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6370 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6374 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6378 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6380 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6383 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6384 if(!IsHelping(user
))
6385 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6387 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6391 new_owner_old_access
= new_owner
->access
;
6392 if(new_owner
->access
>= UL_COOWNER
)
6393 co_access
= new_owner
->access
;
6395 co_access
= UL_COOWNER
;
6396 new_owner
->access
= UL_OWNER
;
6398 curr_user
->access
= co_access
;
6399 cData
->ownerTransfer
= now
;
6401 giveownership
= calloc(1, sizeof(*giveownership
));
6402 giveownership
->issued
= now
;
6403 giveownership
->old_owner
= curr_user
->handle
->handle
;
6404 giveownership
->target
= new_owner_hi
->handle
;
6405 giveownership
->target_access
= new_owner_old_access
;
6408 if(argc
> (2 + force
))
6410 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6411 giveownership
->reason
= strdup(transfer_reason
);
6413 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6416 giveownership
->previous
= channel
->channel_info
->giveownership
;
6417 channel
->channel_info
->giveownership
= giveownership
;
6419 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6420 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6421 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6426 chanserv_expire_user_suspension(void *data
)
6428 struct userData
*target
= data
;
6430 target
->expires
= 0;
6431 target
->flags
&= ~USER_SUSPENDED
;
6434 static CHANSERV_FUNC(cmd_suspend
)
6436 struct handle_info
*hi
;
6437 struct userData
*self
, *target
;
6441 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6442 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6443 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6445 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6448 if(target
->access
>= self
->access
)
6450 reply("MSG_USER_OUTRANKED", hi
->handle
);
6453 if(target
->flags
& USER_SUSPENDED
)
6455 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6460 target
->present
= 0;
6463 if(!strcmp(argv
[2], "0"))
6467 unsigned int duration
;
6468 if(!(duration
= ParseInterval(argv
[2])))
6470 reply("MSG_INVALID_DURATION", argv
[2]);
6473 expiry
= now
+ duration
;
6476 target
->expires
= expiry
;
6479 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6481 target
->flags
|= USER_SUSPENDED
;
6482 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6486 static CHANSERV_FUNC(cmd_unsuspend
)
6488 struct handle_info
*hi
;
6489 struct userData
*self
, *target
;
6492 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6493 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6494 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6496 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6499 if(target
->access
>= self
->access
)
6501 reply("MSG_USER_OUTRANKED", hi
->handle
);
6504 if(!(target
->flags
& USER_SUSPENDED
))
6506 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6509 target
->flags
&= ~USER_SUSPENDED
;
6510 scan_user_presence(target
, NULL
);
6511 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6512 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6516 static MODCMD_FUNC(cmd_deleteme
)
6518 struct handle_info
*hi
;
6519 struct userData
*target
;
6520 const char *confirm_string
;
6521 unsigned short access
;
6524 hi
= user
->handle_info
;
6525 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6527 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6530 if(target
->access
== UL_OWNER
)
6532 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6535 confirm_string
= make_confirmation_string(target
);
6536 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6538 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6541 access
= target
->access
;
6542 channel_name
= strdup(channel
->name
);
6543 del_channel_user(target
, 1);
6544 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6550 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6552 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6553 struct chanData
*cData
;
6556 for(cData
= channelList
; cData
; cData
= cData
->next
)
6558 if(IsSuspended(cData
))
6560 opt
= cData
->chOpts
[chTopicRefresh
];
6563 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6566 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6567 cData
->last_refresh
= refresh_num
;
6569 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6573 chanserv_auto_resync(UNUSED_ARG(void *data
))
6575 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6576 struct chanData
*cData
;
6579 for(cData
= channelList
; cData
; cData
= cData
->next
)
6581 if(IsSuspended(cData
)) continue;
6582 opt
= cData
->chOpts
[chResync
];
6583 if(opt
== 'n') continue;
6584 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6585 resync_channel(cData
->channel
);
6586 cData
->last_resync
= refresh_num
;
6588 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6591 static CHANSERV_FUNC(cmd_unf
)
6595 char response
[MAXLEN
];
6596 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6597 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6598 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6601 reply("CSMSG_UNF_RESPONSE");
6605 static CHANSERV_FUNC(cmd_ping
)
6609 char response
[MAXLEN
];
6610 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6611 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6612 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6615 reply("CSMSG_PING_RESPONSE");
6619 static CHANSERV_FUNC(cmd_wut
)
6623 char response
[MAXLEN
];
6624 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6625 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6626 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6629 reply("CSMSG_WUT_RESPONSE");
6634 static CHANSERV_FUNC(cmd_8ball
)
6636 unsigned int i
, j
, accum
;
6641 for(i
=1; i
<argc
; i
++)
6642 for(j
=0; argv
[i
][j
]; j
++)
6643 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6644 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6647 char response
[MAXLEN
];
6648 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6649 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6652 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6656 #else /* Use cool 8ball instead */
6658 void eightball(char *outcome
, int method
, unsigned int seed
)
6662 #define NUMOFCOLORS 18
6663 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6664 "white", "black", "grey", "brown",
6665 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6666 "fuchsia","turquoise","magenta", "cyan"};
6667 #define NUMOFLOCATIONS 50
6668 char balllocations
[50][55] = {
6669 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6670 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6671 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6672 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6673 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6674 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6675 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6676 "your bra", "your hair", "your bed", "the couch", "the wall",
6677 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6678 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6679 #define NUMOFPREPS 15
6680 char ballpreps
[50][50] = {
6681 "Near", "Somewhere near", "In", "In", "In",
6682 "In", "Hiding in", "Under", "Next to", "Over",
6683 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6684 #define NUMOFNUMS 34
6685 char ballnums
[50][50] = {
6686 "A hundred", "A thousand", "A few", "42",
6687 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6688 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6689 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6691 #define NUMOFMULTS 8
6692 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6695 * 0: normal (Not used in x3)
6702 if (method
== 1) /* A Color */
6706 answer
= (rand() % 12); /* Make sure this is the # of entries */
6709 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6711 case 1: strcpy(tmp
, "Sort of a light %s color.");
6713 case 2: strcpy(tmp
, "Dark and dreary %s.");
6715 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6717 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6719 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6721 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6723 case 10: strcpy(tmp
, "Solid %s.");
6725 case 11: strcpy(tmp
, "Transparent %s.");
6727 default: strcpy(outcome
, "An invalid random number was generated.");
6730 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6733 else if (method
== 2) /* Location */
6735 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6737 else if (method
== 3) /* Number of ___ */
6739 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6743 //Debug(DBGWARNING, "Error in 8ball.");
6748 static CHANSERV_FUNC(cmd_8ball
)
6750 char *word1
, *word2
, *word3
;
6751 static char eb
[MAXLEN
];
6752 unsigned int accum
, i
, j
;
6756 for(i
=1; i
<argc
; i
++)
6757 for(j
=0; argv
[i
][j
]; j
++)
6758 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6760 accum
+= time(NULL
)/3600;
6762 word2
= argc
>2?argv
[2]:"";
6763 word3
= argc
>3?argv
[3]:"";
6766 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6767 eightball(eb
, 1, accum
);
6768 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6769 eightball(eb
, 1, accum
);
6770 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6771 eightball(eb
, 1, accum
);
6772 /*** LOCATION *****/
6777 (strcasecmp(word1
, "where") == 0) &&
6778 (strcasecmp(word2
, "is") == 0)
6782 strcasecmp(word1
, "where's") == 0
6785 eightball(eb
, 2, accum
);
6787 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6788 eightball(eb
, 3, accum
);
6792 /* Generic 8ball question.. so pull from x3.conf srvx style */
6795 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6798 char response
[MAXLEN
];
6799 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6800 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6803 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6809 char response
[MAXLEN
];
6810 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6811 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6814 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6819 static CHANSERV_FUNC(cmd_d
)
6821 unsigned long sides
, count
, modifier
, ii
, total
;
6822 char response
[MAXLEN
], *sep
;
6826 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6836 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6837 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6841 else if((sep
[0] == '-') && isdigit(sep
[1]))
6842 modifier
= strtoul(sep
, NULL
, 10);
6843 else if((sep
[0] == '+') && isdigit(sep
[1]))
6844 modifier
= strtoul(sep
+1, NULL
, 10);
6851 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6856 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6859 for(total
= ii
= 0; ii
< count
; ++ii
)
6860 total
+= (rand() % sides
) + 1;
6863 if((count
> 1) || modifier
)
6865 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6866 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6870 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6871 sprintf(response
, fmt
, total
, sides
);
6874 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6876 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6880 static CHANSERV_FUNC(cmd_huggle
)
6882 /* CTCP must be via PRIVMSG, never notice */
6884 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6886 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6890 static CHANSERV_FUNC(cmd_calc
)
6892 char response
[MAXLEN
];
6895 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6898 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6900 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6905 chanserv_adjust_limit(void *data
)
6907 struct mod_chanmode change
;
6908 struct chanData
*cData
= data
;
6909 struct chanNode
*channel
= cData
->channel
;
6912 if(IsSuspended(cData
))
6915 cData
->limitAdjusted
= now
;
6916 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6917 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6919 if(limit
> cData
->modes
.new_limit
)
6920 limit
= cData
->modes
.new_limit
;
6921 else if(limit
== cData
->modes
.new_limit
)
6925 mod_chanmode_init(&change
);
6926 change
.modes_set
= MODE_LIMIT
;
6927 change
.new_limit
= limit
;
6928 mod_chanmode_announce(chanserv
, channel
, &change
);
6932 handle_new_channel(struct chanNode
*channel
)
6934 struct chanData
*cData
;
6936 if(!(cData
= channel
->channel_info
))
6939 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6940 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6942 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6943 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6946 /* Welcome to my worst nightmare. Warning: Read (or modify)
6947 the code below at your own risk. */
6949 handle_join(struct modeNode
*mNode
)
6951 struct mod_chanmode change
;
6952 struct userNode
*user
= mNode
->user
;
6953 struct chanNode
*channel
= mNode
->channel
;
6954 struct chanData
*cData
;
6955 struct userData
*uData
= NULL
;
6956 struct banData
*bData
;
6957 struct handle_info
*handle
;
6958 unsigned int modes
= 0, info
= 0;
6961 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6964 cData
= channel
->channel_info
;
6965 if(channel
->members
.used
> cData
->max
)
6966 cData
->max
= channel
->members
.used
;
6969 /* Check for bans. If they're joining through a ban, one of two
6971 * 1: Join during a netburst, by riding the break. Kick them
6972 * unless they have ops or voice in the channel.
6973 * 2: They're allowed to join through the ban (an invite in
6974 * ircu2.10, or a +e on Hybrid, or something).
6975 * If they're not joining through a ban, and the banlist is not
6976 * full, see if they're on the banlist for the channel. If so,
6979 if(user
->uplink
->burst
&& !mNode
->modes
)
6982 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
6984 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
6986 /* Riding a netburst. Naughty. */
6987 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
6994 mod_chanmode_init(&change
);
6996 if(channel
->banlist
.used
< MAXBANS
)
6998 /* Not joining through a ban. */
6999 for(bData
= cData
->bans
;
7000 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7001 bData
= bData
->next
);
7005 char kick_reason
[MAXLEN
];
7006 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7008 bData
->triggered
= now
;
7009 if(bData
!= cData
->bans
)
7011 /* Shuffle the ban to the head of the list. */
7013 bData
->next
->prev
= bData
->prev
;
7015 bData
->prev
->next
= bData
->next
;
7018 bData
->next
= cData
->bans
;
7021 cData
->bans
->prev
= bData
;
7022 cData
->bans
= bData
;
7025 change
.args
[0].mode
= MODE_BAN
;
7026 change
.args
[0].u
.hostmask
= bData
->mask
;
7027 mod_chanmode_announce(chanserv
, channel
, &change
);
7028 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7033 /* ChanServ will not modify the limits in join-flooded channels.
7034 It will also skip DynLimit processing when the user (or srvx)
7035 is bursting in, because there are likely more incoming. */
7036 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7037 && !user
->uplink
->burst
7038 && !channel
->join_flooded
7039 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7041 /* The user count has begun "bumping" into the channel limit,
7042 so set a timer to raise the limit a bit. Any previous
7043 timers are removed so three incoming users within the delay
7044 results in one limit change, not three. */
7046 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7047 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7050 /* Give automodes exept during join-floods */
7051 if(!channel
->join_flooded
)
7053 if(cData
->chOpts
[chAutomode
] == 'v')
7054 modes
|= MODE_VOICE
;
7055 else if(cData
->chOpts
[chAutomode
] == 'h')
7056 modes
|= MODE_HALFOP
;
7057 else if(cData
->chOpts
[chAutomode
] == 'o')
7058 modes
|= MODE_CHANOP
;
7061 greeting
= cData
->greeting
;
7062 if(user
->handle_info
)
7064 handle
= user
->handle_info
;
7066 if(IsHelper(user
) && !IsHelping(user
))
7069 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7071 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7073 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7079 uData
= GetTrueChannelAccess(cData
, handle
);
7080 if(uData
&& !IsUserSuspended(uData
))
7082 /* non users getting automodes are handled above. */
7083 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7085 if(uData
->access
>= UL_OP
)
7086 modes
|= MODE_CHANOP
;
7087 else if(uData
->access
>= UL_HALFOP
)
7088 modes
|= MODE_HALFOP
;
7089 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7090 modes
|= MODE_VOICE
;
7092 if(uData
->access
>= UL_PRESENT
)
7093 cData
->visited
= now
;
7094 if(cData
->user_greeting
)
7095 greeting
= cData
->user_greeting
;
7097 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7098 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7106 /* If user joining normally (not during burst), apply op or voice,
7107 * and send greeting/userinfo as appropriate.
7109 if(!user
->uplink
->burst
)
7113 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7114 if(modes & MODE_CHANOP) {
7115 modes &= ~MODE_HALFOP;
7116 modes &= ~MODE_VOICE;
7119 change
.args
[0].mode
= modes
;
7120 change
.args
[0].u
.member
= mNode
;
7121 mod_chanmode_announce(chanserv
, channel
, &change
);
7124 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7126 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7132 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7134 struct mod_chanmode change
;
7135 struct userData
*channel
;
7136 unsigned int ii
, jj
, i
;
7138 if(!user
->handle_info
)
7141 mod_chanmode_init(&change
);
7143 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7145 struct chanNode
*cn
;
7146 struct modeNode
*mn
;
7147 if(IsUserSuspended(channel
)
7148 || IsSuspended(channel
->channel
)
7149 || !(cn
= channel
->channel
->channel
))
7152 mn
= GetUserMode(cn
, user
);
7155 if(!IsUserSuspended(channel
)
7156 && IsUserAutoInvite(channel
)
7157 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7159 && !user
->uplink
->burst
)
7160 irc_invite(chanserv
, user
, cn
);
7164 if(channel
->access
>= UL_PRESENT
)
7165 channel
->channel
->visited
= now
;
7167 if(IsUserAutoOp(channel
))
7169 if(channel
->access
>= UL_OP
)
7170 change
.args
[0].mode
= MODE_CHANOP
;
7171 else if(channel
->access
>= UL_HALFOP
)
7172 change
.args
[0].mode
= MODE_HALFOP
;
7173 else if(channel
->access
>= UL_PEON
)
7174 change
.args
[0].mode
= MODE_VOICE
;
7176 change
.args
[0].mode
= 0;
7177 change
.args
[0].u
.member
= mn
;
7178 if(change
.args
[0].mode
)
7179 mod_chanmode_announce(chanserv
, cn
, &change
);
7182 channel
->seen
= now
;
7183 channel
->present
= 1;
7186 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7188 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7189 struct banData
*ban
;
7191 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7192 || !channel
->channel_info
7193 || IsSuspended(channel
->channel_info
))
7195 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7196 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7198 if(jj
< channel
->banlist
.used
)
7200 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7202 char kick_reason
[MAXLEN
];
7203 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7205 change
.args
[0].mode
= MODE_BAN
;
7206 change
.args
[0].u
.hostmask
= ban
->mask
;
7207 mod_chanmode_announce(chanserv
, channel
, &change
);
7208 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7209 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7210 ban
->triggered
= now
;
7215 if(IsSupportHelper(user
))
7217 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7219 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7221 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7227 if (user
->handle_info
->ignores
->used
) {
7228 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7229 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7233 if (user
->handle_info
->epithet
)
7234 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7238 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7240 struct chanData
*cData
;
7241 struct userData
*uData
;
7243 cData
= mn
->channel
->channel_info
;
7244 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7247 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7249 /* Allow for a bit of padding so that the limit doesn't
7250 track the user count exactly, which could get annoying. */
7251 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7253 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7254 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7258 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7260 scan_user_presence(uData
, mn
->user
);
7264 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7266 unsigned int ii
, jj
;
7267 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7269 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7270 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7272 if(jj
< mn
->user
->channels
.used
)
7275 if(ii
== chanserv_conf
.support_channels
.used
)
7276 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7281 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7283 struct userData
*uData
;
7285 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7286 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7287 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7290 if(protect_user(victim
, kicker
, channel
->channel_info
))
7292 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7293 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7296 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7301 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7303 struct chanData
*cData
;
7305 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7308 cData
= channel
->channel_info
;
7309 if(bad_topic(channel
, user
, channel
->topic
))
7310 { /* User doesnt have privs to set topics. Undo it */
7311 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7312 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7315 /* If there is a topic mask set, and the new topic doesnt match,
7316 * set the topic to mask + new_topic */
7317 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7319 char new_topic
[TOPICLEN
+1];
7320 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7323 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7324 /* and fall through to topicsnarf code below.. */
7326 else /* Topic couldnt fit into mask, was too long */
7328 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7329 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7330 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7334 /* With topicsnarf, grab the topic and save it as the default topic. */
7335 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7338 cData
->topic
= strdup(channel
->topic
);
7344 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7346 struct mod_chanmode
*bounce
= NULL
;
7347 unsigned int bnc
, ii
;
7350 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7353 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7354 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7356 char correct
[MAXLEN
];
7357 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7358 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7359 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7361 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7363 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7365 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7366 if(!protect_user(victim
, user
, channel
->channel_info
))
7369 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7372 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7373 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7374 if(bounce
->args
[bnc
].u
.member
)
7378 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7379 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7381 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7383 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7385 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7386 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7389 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7390 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7391 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7394 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7396 const char *ban
= change
->args
[ii
].u
.hostmask
;
7397 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7400 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7401 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7402 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7404 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7409 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7410 mod_chanmode_announce(chanserv
, channel
, bounce
);
7411 for(ii
= 0; ii
< change
->argc
; ++ii
)
7412 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7413 free((char*)bounce
->args
[ii
].u
.hostmask
);
7414 mod_chanmode_free(bounce
);
7419 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7421 struct chanNode
*channel
;
7422 struct banData
*bData
;
7423 struct mod_chanmode change
;
7424 unsigned int ii
, jj
;
7425 char kick_reason
[MAXLEN
];
7427 mod_chanmode_init(&change
);
7429 change
.args
[0].mode
= MODE_BAN
;
7430 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7432 channel
= user
->channels
.list
[ii
]->channel
;
7433 /* Need not check for bans if they're opped or voiced. */
7434 /* TODO: does this make sense in automode v, h, and o? *
7435 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7436 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7438 /* Need not check for bans unless channel registration is active. */
7439 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7441 /* Look for a matching ban already on the channel. */
7442 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7443 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7445 /* Need not act if we found one. */
7446 if(jj
< channel
->banlist
.used
)
7448 /* Look for a matching ban in this channel. */
7449 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7451 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7453 change
.args
[0].u
.hostmask
= bData
->mask
;
7454 mod_chanmode_announce(chanserv
, channel
, &change
);
7455 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7456 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7457 bData
->triggered
= now
;
7458 break; /* we don't need to check any more bans in the channel */
7463 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7465 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7469 dict_remove2(handle_dnrs
, old_handle
, 1);
7470 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7471 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7476 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7478 struct userNode
*h_user
;
7480 if(handle
->channels
)
7482 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7483 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7485 while(handle
->channels
)
7486 del_channel_user(handle
->channels
, 1);
7491 handle_server_link(UNUSED_ARG(struct server
*server
))
7493 struct chanData
*cData
;
7495 for(cData
= channelList
; cData
; cData
= cData
->next
)
7497 if(!IsSuspended(cData
))
7498 cData
->may_opchan
= 1;
7499 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7500 && !cData
->channel
->join_flooded
7501 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7502 < chanserv_conf
.adjust_threshold
))
7504 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7505 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7511 chanserv_conf_read(void)
7515 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7516 struct mod_chanmode
*change
;
7517 struct string_list
*strlist
;
7518 struct chanNode
*chan
;
7521 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7523 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7526 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7527 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7528 chanserv_conf
.support_channels
.used
= 0;
7529 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7531 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7533 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7536 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7538 channelList_append(&chanserv_conf
.support_channels
, chan
);
7541 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7544 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7547 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7549 channelList_append(&chanserv_conf
.support_channels
, chan
);
7551 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7552 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7553 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7554 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7555 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7556 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7557 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7558 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7559 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7560 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7561 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7562 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7563 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7564 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7565 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7566 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7567 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7568 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7569 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7570 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7571 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7572 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7573 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7574 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7575 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7577 NickChange(chanserv
, str
, 0);
7578 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7579 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7580 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7581 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7582 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7583 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7584 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7585 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7586 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7587 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7588 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7589 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7590 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7591 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7592 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7593 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7594 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7595 god_timeout
= str
? ParseInterval(str
) : 60*15;
7596 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7599 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7600 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7601 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7602 && (change
->argc
< 2))
7604 chanserv_conf
.default_modes
= *change
;
7605 mod_chanmode_free(change
);
7607 free_string_list(chanserv_conf
.set_shows
);
7608 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7610 strlist
= string_list_copy(strlist
);
7613 static const char *list
[] = {
7614 /* free form text */
7615 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7616 /* options based on user level */
7617 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7618 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7619 /* multiple choice options */
7620 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7621 /* binary options */
7622 "DynLimit", "NoDelete", "BanTimeout",
7627 strlist
= alloc_string_list(ArrayLength(list
)-1);
7628 for(ii
=0; list
[ii
]; ii
++)
7629 string_list_append(strlist
, strdup(list
[ii
]));
7631 chanserv_conf
.set_shows
= strlist
;
7632 /* We don't look things up now, in case the list refers to options
7633 * defined by modules initialized after this point. Just mark the
7634 * function list as invalid, so it will be initialized.
7636 set_shows_list
.used
= 0;
7637 free_string_list(chanserv_conf
.eightball
);
7638 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7641 strlist
= string_list_copy(strlist
);
7645 strlist
= alloc_string_list(4);
7646 string_list_append(strlist
, strdup("Yes."));
7647 string_list_append(strlist
, strdup("No."));
7648 string_list_append(strlist
, strdup("Maybe so."));
7650 chanserv_conf
.eightball
= strlist
;
7651 free_string_list(chanserv_conf
.old_ban_names
);
7652 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7654 strlist
= string_list_copy(strlist
);
7656 strlist
= alloc_string_list(2);
7657 chanserv_conf
.old_ban_names
= strlist
;
7658 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7659 off_channel
= str
? atoi(str
) : 0;
7663 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7666 struct note_type
*ntype
;
7669 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7671 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7674 if(!(ntype
= chanserv_create_note_type(key
)))
7676 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7680 /* Figure out set access */
7681 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7683 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7684 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7686 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7688 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7689 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7691 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7693 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7697 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7698 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7699 ntype
->set_access
.min_opserv
= 0;
7702 /* Figure out visibility */
7703 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7704 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7705 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7706 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7707 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7708 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7709 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7710 ntype
->visible_type
= NOTE_VIS_ALL
;
7712 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7714 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7715 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7719 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7721 struct handle_info
*handle
;
7722 struct userData
*uData
;
7723 char *seen
, *inf
, *flags
, *expires
;
7725 unsigned short access
;
7727 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7729 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7733 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7734 if(access
> UL_OWNER
)
7736 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7740 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7741 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7742 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7743 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7744 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7745 handle
= get_handle_info(key
);
7748 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7752 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7753 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7754 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7756 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7758 if(uData
->expires
> now
)
7759 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7761 uData
->flags
&= ~USER_SUSPENDED
;
7764 /* Upgrade: set autoop to the inverse of noautoop */
7765 if(chanserv_read_version
< 2)
7767 /* if noautoop is true, set autoop false, and vice versa */
7768 if(uData
->flags
& USER_NOAUTO_OP
)
7769 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7771 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7772 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
);
7778 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7780 struct banData
*bData
;
7781 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7782 time_t set_time
, triggered_time
, expires_time
;
7784 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7786 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7790 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7791 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7792 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7793 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7794 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7795 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7796 if (!reason
|| !owner
)
7799 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7800 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7802 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7804 expires_time
= set_time
+ atoi(s_duration
);
7808 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7811 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7814 static struct suspended
*
7815 chanserv_read_suspended(dict_t obj
)
7817 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7821 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7822 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7823 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7824 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7825 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7826 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7827 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7828 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7829 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7830 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7834 static struct giveownership
*
7835 chanserv_read_giveownership(dict_t obj
)
7837 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7841 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7842 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7844 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7846 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7847 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7849 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7850 giveownership
->reason
= str
? strdup(str
) : NULL
;
7851 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7852 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7854 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7855 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7856 return giveownership
;
7860 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7862 struct suspended
*suspended
;
7863 struct giveownership
*giveownership
;
7864 struct mod_chanmode
*modes
;
7865 struct chanNode
*cNode
;
7866 struct chanData
*cData
;
7867 struct dict
*channel
, *obj
;
7868 char *str
, *argv
[10];
7872 channel
= hir
->d
.object
;
7874 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7877 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7880 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7883 cData
= register_channel(cNode
, str
);
7886 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7890 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7892 enum levelOption lvlOpt
;
7893 enum charOption chOpt
;
7895 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7896 cData
->flags
= atoi(str
);
7898 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7900 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7902 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7903 else if(levelOptions
[lvlOpt
].old_flag
)
7905 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7906 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7908 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7912 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7914 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7916 cData
->chOpts
[chOpt
] = str
[0];
7919 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7921 enum levelOption lvlOpt
;
7922 enum charOption chOpt
;
7925 cData
->flags
= base64toint(str
, 5);
7926 count
= strlen(str
+= 5);
7927 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7930 if(levelOptions
[lvlOpt
].old_flag
)
7932 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7933 lvl
= levelOptions
[lvlOpt
].flag_value
;
7935 lvl
= levelOptions
[lvlOpt
].default_value
;
7937 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7939 case 'c': lvl
= UL_COOWNER
; break;
7940 case 'm': lvl
= UL_MANAGER
; break;
7941 case 'n': lvl
= UL_OWNER
+1; break;
7942 case 'o': lvl
= UL_OP
; break;
7943 case 'p': lvl
= UL_PEON
; break;
7944 case 'h': lvl
= UL_HALFOP
; break;
7945 case 'w': lvl
= UL_OWNER
; break;
7946 default: lvl
= 0; break;
7948 cData
->lvlOpts
[lvlOpt
] = lvl
;
7950 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7951 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7954 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7956 suspended
= chanserv_read_suspended(obj
);
7957 cData
->suspended
= suspended
;
7958 suspended
->cData
= cData
;
7959 /* We could use suspended->expires and suspended->revoked to
7960 * set the CHANNEL_SUSPENDED flag, but we don't. */
7962 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7964 suspended
= calloc(1, sizeof(*suspended
));
7965 suspended
->issued
= 0;
7966 suspended
->revoked
= 0;
7967 suspended
->suspender
= strdup(str
);
7968 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7969 suspended
->expires
= str
? atoi(str
) : 0;
7970 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7971 suspended
->reason
= strdup(str
? str
: "No reason");
7972 suspended
->previous
= NULL
;
7973 cData
->suspended
= suspended
;
7974 suspended
->cData
= cData
;
7978 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7979 suspended
= NULL
; /* to squelch a warning */
7982 if(IsSuspended(cData
)) {
7983 if(suspended
->expires
> now
)
7984 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7985 else if(suspended
->expires
)
7986 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7989 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
7991 giveownership
= chanserv_read_giveownership(obj
);
7992 cData
->giveownership
= giveownership
;
7995 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7996 struct mod_chanmode change
;
7997 mod_chanmode_init(&change
);
7999 change
.args
[0].mode
= MODE_CHANOP
;
8000 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8001 mod_chanmode_announce(chanserv
, cNode
, &change
);
8004 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8005 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8006 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8007 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8008 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8009 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8010 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8011 cData
->max
= str
? atoi(str
) : 0;
8012 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8013 cData
->greeting
= str
? strdup(str
) : NULL
;
8014 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8015 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8016 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8017 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8018 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8019 cData
->topic
= str
? strdup(str
) : NULL
;
8021 if(!IsSuspended(cData
)
8022 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8023 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8024 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8025 cData
->modes
= *modes
;
8027 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8028 if(cData
->modes
.argc
> 1)
8029 cData
->modes
.argc
= 1;
8030 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8031 mod_chanmode_free(modes
);
8034 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8035 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8036 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8038 if(!cData
->users
&& !IsProtected(cData
))
8040 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8041 unregister_channel(cData
, "has empty user list.");
8045 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8046 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8047 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8049 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8050 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8052 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8053 struct record_data
*rd
= iter_data(it
);
8054 const char *note
, *setter
;
8056 if(rd
->type
!= RECDB_OBJECT
)
8058 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8062 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8064 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8066 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8070 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8071 if(!setter
) setter
= "<unknown>";
8072 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8080 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8082 const char *setter
, *reason
, *str
;
8083 struct do_not_register
*dnr
;
8085 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8088 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8091 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8094 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8097 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8100 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8102 dnr
->set
= atoi(str
);
8108 chanserv_version_read(struct dict
*section
)
8112 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8114 chanserv_read_version
= atoi(str
);
8115 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8119 chanserv_saxdb_read(struct dict
*database
)
8121 struct dict
*section
;
8124 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8125 chanserv_version_read(section
);
8127 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8128 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8129 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8131 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8132 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8133 chanserv_channel_read(iter_key(it
), iter_data(it
));
8135 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8136 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8137 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8143 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8145 int high_present
= 0;
8146 saxdb_start_record(ctx
, KEY_USERS
, 1);
8147 for(; uData
; uData
= uData
->next
)
8149 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8151 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8152 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8153 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8155 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8157 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8159 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8160 saxdb_end_record(ctx
);
8162 saxdb_end_record(ctx
);
8163 return high_present
;
8167 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8171 saxdb_start_record(ctx
, KEY_BANS
, 1);
8172 for(; bData
; bData
= bData
->next
)
8174 saxdb_start_record(ctx
, bData
->mask
, 0);
8175 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8176 if(bData
->triggered
)
8177 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8179 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8181 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8183 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8184 saxdb_end_record(ctx
);
8186 saxdb_end_record(ctx
);
8190 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8192 saxdb_start_record(ctx
, name
, 0);
8193 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8194 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8196 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8198 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8200 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8202 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8203 saxdb_end_record(ctx
);
8207 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8209 saxdb_start_record(ctx
, name
, 0);
8210 if(giveownership
->staff_issuer
)
8211 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8212 if(giveownership
->old_owner
)
8213 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8214 if(giveownership
->target
)
8215 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8216 if(giveownership
->target_access
)
8217 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8218 if(giveownership
->reason
)
8219 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8220 if(giveownership
->issued
)
8221 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8222 if(giveownership
->previous
)
8223 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8224 saxdb_end_record(ctx
);
8228 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8232 enum levelOption lvlOpt
;
8233 enum charOption chOpt
;
8235 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8237 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8238 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8240 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8241 if(channel
->registrar
)
8242 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8243 if(channel
->greeting
)
8244 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8245 if(channel
->user_greeting
)
8246 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8247 if(channel
->topic_mask
)
8248 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8249 if(channel
->suspended
)
8250 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8251 if(channel
->giveownership
)
8252 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8254 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8255 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8256 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8257 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8258 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8260 buf
[0] = channel
->chOpts
[chOpt
];
8262 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8264 saxdb_end_record(ctx
);
8266 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8268 mod_chanmode_format(&channel
->modes
, buf
);
8269 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8272 high_present
= chanserv_write_users(ctx
, channel
->users
);
8273 chanserv_write_bans(ctx
, channel
->bans
);
8275 if(dict_size(channel
->notes
))
8279 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8280 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8282 struct note
*note
= iter_data(it
);
8283 saxdb_start_record(ctx
, iter_key(it
), 0);
8284 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8285 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8286 saxdb_end_record(ctx
);
8288 saxdb_end_record(ctx
);
8291 if(channel
->ownerTransfer
)
8292 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8293 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8294 saxdb_end_record(ctx
);
8298 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8302 saxdb_start_record(ctx
, ntype
->name
, 0);
8303 switch(ntype
->set_access_type
)
8305 case NOTE_SET_CHANNEL_ACCESS
:
8306 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8308 case NOTE_SET_CHANNEL_SETTER
:
8309 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8311 case NOTE_SET_PRIVILEGED
: default:
8312 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8315 switch(ntype
->visible_type
)
8317 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8318 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8319 case NOTE_VIS_PRIVILEGED
:
8320 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8322 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8323 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8324 saxdb_end_record(ctx
);
8328 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8330 struct do_not_register
*dnr
;
8333 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8335 dnr
= iter_data(it
);
8336 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8338 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8339 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8340 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8341 saxdb_end_record(ctx
);
8346 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8349 struct chanData
*channel
;
8351 /* Version Control*/
8352 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8353 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8354 saxdb_end_record(ctx
);
8357 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8358 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8359 chanserv_write_note_type(ctx
, iter_data(it
));
8360 saxdb_end_record(ctx
);
8363 saxdb_start_record(ctx
, KEY_DNR
, 1);
8364 write_dnrs_helper(ctx
, handle_dnrs
);
8365 write_dnrs_helper(ctx
, plain_dnrs
);
8366 write_dnrs_helper(ctx
, mask_dnrs
);
8367 saxdb_end_record(ctx
);
8370 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8371 for(channel
= channelList
; channel
; channel
= channel
->next
)
8372 chanserv_write_channel(ctx
, channel
);
8373 saxdb_end_record(ctx
);
8379 chanserv_db_cleanup(void) {
8381 unreg_part_func(handle_part
);
8383 unregister_channel(channelList
, "terminating.");
8384 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8385 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8386 free(chanserv_conf
.support_channels
.list
);
8387 dict_delete(handle_dnrs
);
8388 dict_delete(plain_dnrs
);
8389 dict_delete(mask_dnrs
);
8390 dict_delete(note_types
);
8391 free_string_list(chanserv_conf
.eightball
);
8392 free_string_list(chanserv_conf
.old_ban_names
);
8393 free_string_list(chanserv_conf
.set_shows
);
8394 free(set_shows_list
.list
);
8395 free(uset_shows_list
.list
);
8398 struct userData
*helper
= helperList
;
8399 helperList
= helperList
->next
;
8404 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8405 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8406 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8409 init_chanserv(const char *nick
)
8411 struct chanNode
*chan
;
8413 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8414 conf_register_reload(chanserv_conf_read
);
8416 reg_server_link_func(handle_server_link
);
8418 reg_new_channel_func(handle_new_channel
);
8419 reg_join_func(handle_join
);
8420 reg_part_func(handle_part
);
8421 reg_kick_func(handle_kick
);
8422 reg_topic_func(handle_topic
);
8423 reg_mode_change_func(handle_mode
);
8424 reg_nick_change_func(handle_nick_change
);
8426 reg_auth_func(handle_auth
);
8427 reg_handle_rename_func(handle_rename
);
8428 reg_unreg_func(handle_unreg
);
8430 handle_dnrs
= dict_new();
8431 dict_set_free_data(handle_dnrs
, free
);
8432 plain_dnrs
= dict_new();
8433 dict_set_free_data(plain_dnrs
, free
);
8434 mask_dnrs
= dict_new();
8435 dict_set_free_data(mask_dnrs
, free
);
8437 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8438 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8439 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8440 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8441 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8442 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8443 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8444 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8445 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8446 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8448 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8450 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8451 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8453 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8454 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8455 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8456 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8457 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8459 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8460 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8461 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8462 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8463 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8464 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8466 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8467 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8468 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8469 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8471 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8472 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8473 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8474 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8475 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8476 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8477 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8478 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8479 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8480 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8482 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8483 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8484 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8485 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8486 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8487 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8488 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8489 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8490 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8491 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8492 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8493 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8494 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8495 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8497 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8498 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8499 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8500 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8502 /* if you change dellamer access, see also places
8503 * like unbanme which have manager hardcoded. */
8504 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8505 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8507 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8509 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8511 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8512 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8513 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8514 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8515 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8516 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8517 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8518 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8519 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8520 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8521 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8522 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8524 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8525 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8527 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8528 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8529 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8530 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8532 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8533 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8534 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8535 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8536 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8538 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8539 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8540 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8541 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8542 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8543 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8544 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8546 /* Channel options */
8547 DEFINE_CHANNEL_OPTION(defaulttopic
);
8548 DEFINE_CHANNEL_OPTION(topicmask
);
8549 DEFINE_CHANNEL_OPTION(greeting
);
8550 DEFINE_CHANNEL_OPTION(usergreeting
);
8551 DEFINE_CHANNEL_OPTION(modes
);
8552 DEFINE_CHANNEL_OPTION(enfops
);
8553 DEFINE_CHANNEL_OPTION(enfhalfops
);
8554 DEFINE_CHANNEL_OPTION(automode
);
8555 DEFINE_CHANNEL_OPTION(protect
);
8556 DEFINE_CHANNEL_OPTION(enfmodes
);
8557 DEFINE_CHANNEL_OPTION(enftopic
);
8558 DEFINE_CHANNEL_OPTION(pubcmd
);
8559 DEFINE_CHANNEL_OPTION(userinfo
);
8560 DEFINE_CHANNEL_OPTION(dynlimit
);
8561 DEFINE_CHANNEL_OPTION(topicsnarf
);
8562 DEFINE_CHANNEL_OPTION(nodelete
);
8563 DEFINE_CHANNEL_OPTION(toys
);
8564 DEFINE_CHANNEL_OPTION(setters
);
8565 DEFINE_CHANNEL_OPTION(topicrefresh
);
8566 DEFINE_CHANNEL_OPTION(resync
);
8567 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8568 DEFINE_CHANNEL_OPTION(bantimeout
);
8569 DEFINE_CHANNEL_OPTION(inviteme
);
8571 DEFINE_CHANNEL_OPTION(offchannel
);
8572 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8574 /* Alias set topic to set defaulttopic for compatibility. */
8575 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8578 DEFINE_USER_OPTION(autoinvite
);
8579 DEFINE_USER_OPTION(info
);
8580 DEFINE_USER_OPTION(autoop
);
8582 /* Alias uset autovoice to uset autoop. */
8583 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8585 note_types
= dict_new();
8586 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8589 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8590 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8591 service_register(chanserv
)->trigger
= '!';
8592 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8595 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8597 if(chanserv_conf
.channel_expire_frequency
)
8598 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8600 if(chanserv_conf
.ban_timeout_frequency
)
8601 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8603 if(chanserv_conf
.refresh_period
)
8605 time_t next_refresh
;
8606 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8607 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8608 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8611 if (autojoin_channels
&& chanserv
) {
8612 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8613 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8614 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8618 reg_exit_func(chanserv_db_cleanup
);
8619 message_register_table(msgtab
);