1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_VERSION_CONTROL "version_control"
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
65 /* version control paramiter */
66 #define KEY_VERSION_NUMBER "version_number"
68 /* Note type parameters */
69 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
70 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
71 #define KEY_NOTE_SETTER_ACCESS "setter_access"
72 #define KEY_NOTE_VISIBILITY "visibility"
73 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
74 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
75 #define KEY_NOTE_VIS_ALL "all"
76 #define KEY_NOTE_MAX_LENGTH "max_length"
77 #define KEY_NOTE_SETTER "setter"
78 #define KEY_NOTE_NOTE "note"
80 /* Do-not-register channels */
82 #define KEY_DNR_SET "set"
83 #define KEY_DNR_SETTER "setter"
84 #define KEY_DNR_REASON "reason"
87 #define KEY_REGISTERED "registered"
88 #define KEY_REGISTRAR "registrar"
89 #define KEY_SUSPENDED "suspended"
90 #define KEY_PREVIOUS "previous"
91 #define KEY_SUSPENDER "suspender"
92 #define KEY_ISSUED "issued"
93 #define KEY_REVOKED "revoked"
94 #define KEY_SUSPEND_EXPIRES "suspend_expires"
95 #define KEY_SUSPEND_REASON "suspend_reason"
96 #define KEY_GIVEOWNERSHIP "giveownership"
97 #define KEY_STAFF_ISSUER "staff_issuer"
98 #define KEY_OLD_OWNER "old_owner"
99 #define KEY_TARGET "target"
100 #define KEY_TARGET_ACCESS "target_access"
101 #define KEY_VISITED "visited"
102 #define KEY_TOPIC "topic"
103 #define KEY_GREETING "greeting"
104 #define KEY_USER_GREETING "user_greeting"
105 #define KEY_MODES "modes"
106 #define KEY_FLAGS "flags"
107 #define KEY_OPTIONS "options"
108 #define KEY_USERS "users"
109 #define KEY_BANS "bans" /* for lamers */
110 #define KEY_MAX "max"
111 #define KEY_NOTES "notes"
112 #define KEY_TOPIC_MASK "topic_mask"
113 #define KEY_OWNER_TRANSFER "owner_transfer"
116 #define KEY_LEVEL "level"
117 #define KEY_INFO "info"
118 #define KEY_SEEN "seen"
121 #define KEY_OWNER "owner"
122 #define KEY_REASON "reason"
123 #define KEY_SET "set"
124 #define KEY_DURATION "duration"
125 #define KEY_EXPIRES "expires"
126 #define KEY_TRIGGERED "triggered"
128 #define KEY_GOD_TIMEOUT "god_timeout"
130 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
131 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
133 /* Administrative messages */
134 static const struct message_entry msgtab
[] = {
135 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
137 /* Channel registration */
138 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
139 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
140 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
141 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
142 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
143 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
144 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
145 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
147 /* Do-not-register channels */
148 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
150 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
151 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
152 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
153 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
154 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
155 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
156 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
157 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
158 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
160 /* Channel unregistration */
161 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
162 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
163 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
164 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
167 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
168 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
170 /* Channel merging */
171 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
172 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
173 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
174 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
175 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
177 /* Handle unregistration */
178 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
181 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
182 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
183 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
184 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
185 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
186 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
187 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
188 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
189 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
190 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
191 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
192 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
193 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
194 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
196 /* Removing yourself from a channel. */
197 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
198 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
199 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
201 /* User management */
202 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
203 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
204 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
205 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
206 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
207 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
208 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
209 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
210 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
211 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
212 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
213 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
214 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
215 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
216 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
218 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
219 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
220 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
221 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
222 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
223 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
226 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
227 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
228 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
229 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
230 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
231 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
232 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
233 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
234 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
235 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
236 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
237 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
238 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
239 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
240 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
241 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
242 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
244 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
246 /* Channel management */
247 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
248 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
249 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
251 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
252 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
253 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
254 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
255 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
256 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
257 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
259 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
260 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
261 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
262 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
263 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
264 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
265 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
266 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
267 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
268 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
269 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
270 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
271 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
272 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
273 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
274 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
275 { "CSMSG_SET_MODES", "$bModes $b %s" },
276 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
277 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
278 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
279 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
280 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
281 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
282 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
283 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
284 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
285 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
286 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
287 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
288 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
289 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
290 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
291 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
292 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
293 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
294 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
296 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
297 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
298 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
299 { "CSMSG_USET_INFO", "$bInfo $b %s" },
301 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
302 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
303 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
304 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
308 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
309 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
310 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
311 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
313 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
314 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
315 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
316 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
317 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
318 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
320 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
321 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
322 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
323 { "CSMSG_PROTECT_NONE", "No users will be protected." },
324 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
325 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
326 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
328 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
329 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
330 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
331 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
332 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
334 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
335 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
336 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
337 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
338 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
340 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
341 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
342 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
343 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
344 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
346 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
347 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
348 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
349 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
350 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
351 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
353 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
354 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
355 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
356 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
357 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
358 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
359 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
360 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
361 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
363 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
364 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
365 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
367 /* Channel userlist */
368 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
369 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
370 /* uncomment if needed to adujust styles (and change code below)
371 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
372 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
373 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
374 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
375 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
376 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
378 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
379 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
380 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
382 /* Channel note list */
383 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
384 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
385 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
386 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
387 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
388 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
389 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
390 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
391 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
392 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
393 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
394 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
395 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
396 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
397 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
399 /* Channel [un]suspension */
400 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
401 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
402 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
403 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
404 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
405 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
406 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
408 /* Access information */
409 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
410 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
411 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
412 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
413 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
414 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
415 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
416 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
417 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
418 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
419 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
421 /* Seen information */
422 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
423 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
424 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
425 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
427 /* Names information */
428 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
429 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
431 /* Channel information */
432 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
433 { "CSMSG_BAR", "----------------------------------------"},
434 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
435 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
436 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
437 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
438 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
439 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
440 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
441 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
442 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
443 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
444 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
445 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
446 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
447 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
452 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
453 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
454 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
455 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
456 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
457 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
458 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
459 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
461 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
462 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
463 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
464 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
465 { "CSMSG_PEEK_OPS", "$bOps:$b" },
466 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
467 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
469 /* Network information */
470 { "CSMSG_NETWORK_INFO", "Network Information:" },
471 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
472 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
473 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
474 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
475 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
476 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
477 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
478 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
481 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
482 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
483 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
485 /* Channel searches */
486 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
487 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
488 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
489 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
491 /* Channel configuration */
492 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
493 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
494 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
495 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
498 { "CSMSG_USER_OPTIONS", "User Options:" },
499 // { "CSMSG_USER_PROTECTED", "That user is protected." },
502 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
503 { "CSMSG_PING_RESPONSE", "Pong!" },
504 { "CSMSG_WUT_RESPONSE", "wut" },
505 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
506 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
507 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
508 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
509 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
510 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
511 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
514 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
515 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
519 /* eject_user and unban_user flags */
520 #define ACTION_KICK 0x0001
521 #define ACTION_BAN 0x0002
522 #define ACTION_ADD_LAMER 0x0004
523 #define ACTION_ADD_TIMED_LAMER 0x0008
524 #define ACTION_UNBAN 0x0010
525 #define ACTION_DEL_LAMER 0x0020
527 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
528 #define MODELEN 40 + KEYLEN
532 #define CSFUNC_ARGS user, channel, argc, argv, cmd
534 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
535 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
536 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
537 reply("MSG_MISSING_PARAMS", argv[0]); \
541 DECLARE_LIST(dnrList
, struct do_not_register
*);
542 DEFINE_LIST(dnrList
, struct do_not_register
*);
544 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
546 struct userNode
*chanserv
;
549 extern struct string_list
*autojoin_channels
;
550 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
551 static struct log_type
*CS_LOG
;
552 struct adduserPending
* adduser_pendings
= NULL
;
553 unsigned int adduser_pendings_count
= 0;
554 unsigned long god_timeout
;
558 struct channelList support_channels
;
559 struct mod_chanmode default_modes
;
561 unsigned long db_backup_frequency
;
562 unsigned long channel_expire_frequency
;
563 unsigned long ban_timeout_frequency
;
566 unsigned int adjust_delay
;
567 long channel_expire_delay
;
568 unsigned int nodelete_level
;
570 unsigned int adjust_threshold
;
571 int join_flood_threshold
;
573 unsigned int greeting_length
;
574 unsigned int refresh_period
;
575 unsigned int giveownership_period
;
577 unsigned int max_owned
;
578 unsigned int max_chan_users
;
579 unsigned int max_chan_bans
; /* lamers */
580 unsigned int max_userinfo_length
;
582 struct string_list
*set_shows
;
583 struct string_list
*eightball
;
584 struct string_list
*old_ban_names
;
586 const char *ctcp_short_ban_duration
;
587 const char *ctcp_long_ban_duration
;
589 const char *irc_operator_epithet
;
590 const char *network_helper_epithet
;
591 const char *support_helper_epithet
;
596 struct userNode
*user
;
597 struct userNode
*bot
;
598 struct chanNode
*channel
;
600 unsigned short lowest
;
601 unsigned short highest
;
602 struct userData
**users
;
603 struct helpfile_table table
;
606 enum note_access_type
608 NOTE_SET_CHANNEL_ACCESS
,
609 NOTE_SET_CHANNEL_SETTER
,
613 enum note_visible_type
616 NOTE_VIS_CHANNEL_USERS
,
622 enum note_access_type set_access_type
;
624 unsigned int min_opserv
;
625 unsigned short min_ulevel
;
627 enum note_visible_type visible_type
;
628 unsigned int max_length
;
635 struct note_type
*type
;
636 char setter
[NICKSERV_HANDLE_LEN
+1];
640 static unsigned int registered_channels
;
641 static unsigned int banCount
;
643 static const struct {
646 unsigned short level
;
648 } accessLevels
[] = { /* MUST be orderd less to most! */
649 { "peon", "Peon", UL_PEON
, '+' },
650 { "halfop", "HalfOp", UL_HALFOP
, '%' },
651 { "op", "Op", UL_OP
, '@' },
652 { "manager", "Manager", UL_MANAGER
, '%' },
653 { "coowner", "Coowner", UL_COOWNER
, '*' },
654 { "owner", "Owner", UL_OWNER
, '!' },
655 { "helper", "BUG:", UL_HELPER
, 'X' }
658 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
659 static const struct {
662 unsigned short default_value
;
663 unsigned int old_idx
;
664 unsigned int old_flag
;
665 unsigned short flag_value
;
667 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
668 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
669 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
670 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
671 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
672 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
673 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
674 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
675 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
678 struct charOptionValues
{
681 } automodeValues
[] = {
682 { 'n', "CSMSG_AUTOMODE_NONE" },
683 { 'y', "CSMSG_AUTOMODE_NORMAL" },
684 { 'v', "CSMSG_AUTOMODE_VOICE" },
685 { 'h', "CSMSG_AUTOMODE_HOP" },
686 { 'o', "CSMSG_AUTOMODE_OP" },
687 { 'm', "CSMSG_AUTOMODE_MUTE" }
688 }, protectValues
[] = {
689 { 'a', "CSMSG_PROTECT_ALL" },
690 { 'e', "CSMSG_PROTECT_EQUAL" },
691 { 'l', "CSMSG_PROTECT_LOWER" },
692 { 'n', "CSMSG_PROTECT_NONE" }
694 { 'd', "CSMSG_TOYS_DISABLED" },
695 { 'n', "CSMSG_TOYS_PRIVATE" },
696 { 'p', "CSMSG_TOYS_PUBLIC" }
697 }, topicRefreshValues
[] = {
698 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
699 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
700 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
701 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
702 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
703 }, ctcpReactionValues
[] = {
704 { 'n', "CSMSG_CTCPREACTION_NONE" },
705 { 'k', "CSMSG_CTCPREACTION_KICK" },
706 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
707 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
708 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
709 }, banTimeoutValues
[] = {
710 { '0', "CSMSG_BANTIMEOUT_NONE" },
711 { '1', "CSMSG_BANTIMEOUT_10M" },
712 { '2', "CSMSG_BANTIMEOUT_2H" },
713 { '3', "CSMSG_BANTIMEOUT_4H" },
714 { '4', "CSMSG_BANTIMEOUT_1D" },
715 { '5', "CSMSG_BANTIMEOUT_1W" }
718 { 'n', "CSMSG_RESYNC_NEVER" },
719 { '1', "CSMSG_RESYNC_3_HOURS" },
720 { '2', "CSMSG_RESYNC_6_HOURS" },
721 { '3', "CSMSG_RESYNC_12_HOURS" },
722 { '4', "CSMSG_RESYNC_24_HOURS" }
725 static const struct {
729 unsigned int old_idx
;
731 struct charOptionValues
*values
;
733 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
734 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
735 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
736 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
737 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
738 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
},
739 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues
), resyncValues
},
742 struct userData
*helperList
;
743 struct chanData
*channelList
;
744 static struct module *chanserv_module
;
745 static unsigned int userCount
;
746 unsigned int chanserv_read_version
= 0; /* db version control */
748 #define CHANSERV_DB_VERSION 2
750 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
751 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
754 user_level_from_name(const char *name
, unsigned short clamp_level
)
756 unsigned int level
= 0, ii
;
758 level
= strtoul(name
, NULL
, 10);
759 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
760 if(!irccasecmp(name
, accessLevels
[ii
].name
))
761 level
= accessLevels
[ii
].level
;
762 if(level
> clamp_level
)
768 user_level_name_from_level(int level
)
776 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
777 if(level
>= accessLevels
[ii
].level
)
778 highest
= accessLevels
[ii
].title
;
784 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
787 *minl
= strtoul(arg
, &sep
, 10);
795 *maxl
= strtoul(sep
+1, &sep
, 10);
803 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
805 struct userData
*uData
, **head
;
807 if(!channel
|| !handle
)
810 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
811 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
813 for(uData
= helperList
;
814 uData
&& uData
->handle
!= handle
;
815 uData
= uData
->next
);
819 uData
= calloc(1, sizeof(struct userData
));
820 uData
->handle
= handle
;
822 uData
->access
= UL_HELPER
;
828 uData
->next
= helperList
;
830 helperList
->prev
= uData
;
838 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
839 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
842 head
= &(channel
->users
);
845 if(uData
&& (uData
!= *head
))
847 /* Shuffle the user to the head of whatever list he was in. */
849 uData
->next
->prev
= uData
->prev
;
851 uData
->prev
->next
= uData
->next
;
857 (**head
).prev
= uData
;
864 /* Returns non-zero if user has at least the minimum access.
865 * exempt_owner is set when handling !set, so the owner can set things
868 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
870 struct userData
*uData
;
871 struct chanData
*cData
= channel
->channel_info
;
872 unsigned short minimum
= cData
->lvlOpts
[opt
];
875 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
878 if(minimum
<= uData
->access
)
880 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
885 /* Scan for other users authenticated to the same handle
886 still in the channel. If so, keep them listed as present.
888 user is optional, if not null, it skips checking that userNode
889 (for the handle_part function) */
891 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
895 if(IsSuspended(uData
->channel
)
896 || IsUserSuspended(uData
)
897 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
909 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
911 unsigned int eflags
, argc
;
913 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
915 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
916 if(!channel
->channel_info
917 || IsSuspended(channel
->channel_info
)
919 || !ircncasecmp(text
, "ACTION ", 7))
921 /* We dont punish people we know -Rubin
922 * * Figure out the minimum level needed to CTCP the channel *
924 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
927 /* If they are a user of the channel, they are exempt */
928 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
930 /* We need to enforce against them; do so. */
933 argv
[1] = user
->nick
;
935 if(GetUserMode(channel
, user
))
936 eflags
|= ACTION_KICK
;
937 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
938 default: case 'n': return;
940 eflags
|= ACTION_KICK
;
943 eflags
|= ACTION_BAN
;
946 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
947 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
950 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
951 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
954 argv
[argc
++] = bad_ctcp_reason
;
955 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
959 chanserv_create_note_type(const char *name
)
961 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
962 strcpy(ntype
->name
, name
);
964 dict_insert(note_types
, ntype
->name
, ntype
);
969 chanserv_deref_note_type(void *data
)
971 struct note_type
*ntype
= data
;
973 if(--ntype
->refs
> 0)
979 chanserv_flush_note_type(struct note_type
*ntype
)
981 struct chanData
*cData
;
982 for(cData
= channelList
; cData
; cData
= cData
->next
)
983 dict_remove(cData
->notes
, ntype
->name
);
987 chanserv_truncate_notes(struct note_type
*ntype
)
989 struct chanData
*cData
;
991 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
993 for(cData
= channelList
; cData
; cData
= cData
->next
) {
994 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
997 if(strlen(note
->note
) <= ntype
->max_length
)
999 dict_remove2(cData
->notes
, ntype
->name
, 1);
1000 note
= realloc(note
, size
);
1001 note
->note
[ntype
->max_length
] = 0;
1002 dict_insert(cData
->notes
, ntype
->name
, note
);
1006 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
1008 static struct note
*
1009 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
1012 unsigned int len
= strlen(text
);
1014 if(len
> type
->max_length
) len
= type
->max_length
;
1015 note
= calloc(1, sizeof(*note
) + len
);
1017 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
1018 memcpy(note
->note
, text
, len
);
1019 note
->note
[len
] = 0;
1020 dict_insert(channel
->notes
, type
->name
, note
);
1026 chanserv_free_note(void *data
)
1028 struct note
*note
= data
;
1030 chanserv_deref_note_type(note
->type
);
1031 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1035 static MODCMD_FUNC(cmd_createnote
) {
1036 struct note_type
*ntype
;
1037 unsigned int arg
= 1, existed
= 0, max_length
;
1039 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1042 ntype
= chanserv_create_note_type(argv
[arg
]);
1043 if(!irccasecmp(argv
[++arg
], "privileged"))
1046 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1047 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1049 else if(!irccasecmp(argv
[arg
], "channel"))
1051 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1054 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1057 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1058 ntype
->set_access
.min_ulevel
= ulvl
;
1060 else if(!irccasecmp(argv
[arg
], "setter"))
1062 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1066 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1070 if(!irccasecmp(argv
[++arg
], "privileged"))
1071 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1072 else if(!irccasecmp(argv
[arg
], "channel_users"))
1073 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1074 else if(!irccasecmp(argv
[arg
], "all"))
1075 ntype
->visible_type
= NOTE_VIS_ALL
;
1077 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1081 if((arg
+1) >= argc
) {
1082 reply("MSG_MISSING_PARAMS", argv
[0]);
1085 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1086 if(max_length
< 20 || max_length
> 450)
1088 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1091 if(existed
&& (max_length
< ntype
->max_length
))
1093 ntype
->max_length
= max_length
;
1094 chanserv_truncate_notes(ntype
);
1096 ntype
->max_length
= max_length
;
1099 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1101 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1106 dict_remove(note_types
, ntype
->name
);
1110 static MODCMD_FUNC(cmd_removenote
) {
1111 struct note_type
*ntype
;
1114 ntype
= dict_find(note_types
, argv
[1], NULL
);
1115 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1118 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1125 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1128 chanserv_flush_note_type(ntype
);
1130 dict_remove(note_types
, argv
[1]);
1131 reply("CSMSG_NOTE_DELETED", argv
[1]);
1136 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1140 if(orig
->modes_set
& change
->modes_clear
)
1142 if(orig
->modes_clear
& change
->modes_set
)
1144 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1145 && strcmp(orig
->new_key
, change
->new_key
))
1147 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1148 && (orig
->new_limit
!= change
->new_limit
))
1153 static char max_length_text
[MAXLEN
+1][16];
1155 static struct helpfile_expansion
1156 chanserv_expand_variable(const char *variable
)
1158 struct helpfile_expansion exp
;
1160 if(!irccasecmp(variable
, "notes"))
1163 exp
.type
= HF_TABLE
;
1164 exp
.value
.table
.length
= 1;
1165 exp
.value
.table
.width
= 3;
1166 exp
.value
.table
.flags
= 0;
1167 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1168 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1169 exp
.value
.table
.contents
[0][0] = "Note Type";
1170 exp
.value
.table
.contents
[0][1] = "Visibility";
1171 exp
.value
.table
.contents
[0][2] = "Max Length";
1172 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1174 struct note_type
*ntype
= iter_data(it
);
1177 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1178 row
= exp
.value
.table
.length
++;
1179 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1180 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1181 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1182 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1184 if(!max_length_text
[ntype
->max_length
][0])
1185 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1186 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1191 exp
.type
= HF_STRING
;
1192 exp
.value
.str
= NULL
;
1196 static struct chanData
*
1197 register_channel(struct chanNode
*cNode
, char *registrar
)
1199 struct chanData
*channel
;
1200 enum levelOption lvlOpt
;
1201 enum charOption chOpt
;
1203 channel
= calloc(1, sizeof(struct chanData
));
1205 channel
->notes
= dict_new();
1206 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1208 channel
->registrar
= strdup(registrar
);
1209 channel
->registered
= now
;
1210 channel
->visited
= now
;
1211 channel
->limitAdjusted
= now
;
1212 channel
->ownerTransfer
= now
;
1213 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1214 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1215 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1216 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1217 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1219 channel
->prev
= NULL
;
1220 channel
->next
= channelList
;
1223 channelList
->prev
= channel
;
1224 channelList
= channel
;
1225 registered_channels
++;
1227 channel
->channel
= cNode
;
1229 cNode
->channel_info
= channel
;
1234 static struct userData
*
1235 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1237 struct userData
*ud
;
1239 if(access
> UL_OWNER
)
1242 ud
= calloc(1, sizeof(*ud
));
1243 ud
->channel
= channel
;
1244 ud
->handle
= handle
;
1246 ud
->access
= access
;
1247 ud
->info
= info
? strdup(info
) : NULL
;
1250 ud
->next
= channel
->users
;
1252 channel
->users
->prev
= ud
;
1253 channel
->users
= ud
;
1255 channel
->userCount
++;
1259 ud
->u_next
= ud
->handle
->channels
;
1261 ud
->u_next
->u_prev
= ud
;
1262 ud
->handle
->channels
= ud
;
1264 ud
->flags
= USER_FLAGS_DEFAULT
;
1268 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1271 del_channel_user(struct userData
*user
, int do_gc
)
1273 struct chanData
*channel
= user
->channel
;
1275 channel
->userCount
--;
1279 user
->prev
->next
= user
->next
;
1281 channel
->users
= user
->next
;
1283 user
->next
->prev
= user
->prev
;
1286 user
->u_prev
->u_next
= user
->u_next
;
1288 user
->handle
->channels
= user
->u_next
;
1290 user
->u_next
->u_prev
= user
->u_prev
;
1294 if(do_gc
&& !channel
->users
&& !IsProtected(channel
)) {
1295 spamserv_cs_unregister(NULL
, channel
->channel
, lost_all_users
, NULL
);
1296 unregister_channel(channel
, "lost all users.");
1300 static struct adduserPending
*
1301 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1303 struct adduserPending
*ap
;
1304 ap
= calloc(1,sizeof(struct adduserPending
));
1305 ap
->channel
= channel
;
1308 ap
->created
= time(NULL
);
1310 /* ap->prev defaults to NULL already.. */
1311 ap
->next
= adduser_pendings
;
1312 if(adduser_pendings
)
1313 adduser_pendings
->prev
= ap
;
1314 adduser_pendings
= ap
;
1315 adduser_pendings_count
++;
1320 del_adduser_pending(struct adduserPending
*ap
)
1323 ap
->prev
->next
= ap
->next
;
1325 adduser_pendings
= ap
->next
;
1328 ap
->next
->prev
= ap
->prev
;
1332 static void expire_adduser_pending();
1334 /* find_adduser_pending(channel, user) will find an arbitrary record
1335 * from user, channel, or user and channel.
1336 * if user or channel are NULL, they will match any records.
1338 static struct adduserPending
*
1339 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1341 struct adduserPending
*ap
;
1343 expire_adduser_pending(); /* why not here.. */
1345 if(!channel
&& !user
) /* 2 nulls matches all */
1346 return(adduser_pendings
);
1347 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1349 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1356 /* Remove all pendings for a user or channel
1358 * called in nickserv.c DelUser() and proto-* unregister_channel()
1361 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1363 struct adduserPending
*ap
;
1365 /* So this is a bit wastefull, i hate dealing with linked lists.
1366 * if its a problem we'll rewrite it right */
1367 while((ap
= find_adduser_pending(channel
, user
))) {
1368 del_adduser_pending(ap
);
1372 /* Called from nickserv.c cmd_auth after someone auths */
1374 process_adduser_pending(struct userNode
*user
)
1376 struct adduserPending
*ap
;
1377 if(!user
->handle_info
)
1378 return; /* not associated with an account */
1379 while((ap
= find_adduser_pending(NULL
, user
)))
1381 struct userData
*actee
;
1382 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1384 /* Already on the userlist. do nothing*/
1388 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1389 scan_user_presence(actee
, NULL
);
1391 del_adduser_pending(ap
);
1396 expire_adduser_pending()
1398 struct adduserPending
*ap
, *ap_next
;
1399 ap
= adduser_pendings
;
1402 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1404 ap_next
= ap
->next
; /* save next */
1405 del_adduser_pending(ap
); /* free and relink */
1406 ap
= ap_next
; /* advance */
1413 static void expire_ban(void *data
);
1416 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1419 unsigned int ii
, l1
, l2
;
1424 bd
= malloc(sizeof(struct banData
));
1426 bd
->channel
= channel
;
1428 bd
->triggered
= triggered
;
1429 bd
->expires
= expires
;
1431 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1433 extern const char *hidden_host_suffix
;
1434 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1438 l2
= strlen(old_name
);
1441 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1443 new_mask
= alloca(MAXLEN
);
1444 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1447 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1449 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1450 bd
->reason
= strdup(reason
);
1453 timeq_add(expires
, expire_ban
, bd
);
1456 bd
->next
= channel
->bans
; /* lamers */
1458 channel
->bans
->prev
= bd
;
1460 channel
->banCount
++;
1467 del_channel_ban(struct banData
*ban
)
1469 ban
->channel
->banCount
--;
1473 ban
->prev
->next
= ban
->next
;
1475 ban
->channel
->bans
= ban
->next
;
1478 ban
->next
->prev
= ban
->prev
;
1481 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1490 expire_ban(void *data
) /* lamer.. */
1492 struct banData
*bd
= data
;
1493 if(!IsSuspended(bd
->channel
))
1495 struct banList bans
;
1496 struct mod_chanmode change
;
1498 bans
= bd
->channel
->channel
->banlist
;
1499 mod_chanmode_init(&change
);
1500 for(ii
=0; ii
<bans
.used
; ii
++)
1502 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1505 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1506 change
.args
[0].u
.hostmask
= bd
->mask
;
1507 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1513 del_channel_ban(bd
);
1516 static void chanserv_expire_suspension(void *data
);
1519 unregister_channel(struct chanData
*channel
, const char *reason
)
1521 struct mod_chanmode change
;
1522 char msgbuf
[MAXLEN
];
1524 /* After channel unregistration, the following must be cleaned
1526 - Channel information.
1528 - Channel bans. (lamers)
1529 - Channel suspension data.
1530 - adduser_pending data.
1531 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1537 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1541 mod_chanmode_init(&change
);
1542 change
.modes_clear
|= MODE_REGISTERED
;
1543 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1546 wipe_adduser_pending(channel
->channel
, NULL
);
1548 while(channel
->users
)
1549 del_channel_user(channel
->users
, 0);
1551 while(channel
->bans
)
1552 del_channel_ban(channel
->bans
);
1554 free(channel
->topic
);
1555 free(channel
->registrar
);
1556 free(channel
->greeting
);
1557 free(channel
->user_greeting
);
1558 free(channel
->topic_mask
);
1561 channel
->prev
->next
= channel
->next
;
1563 channelList
= channel
->next
;
1566 channel
->next
->prev
= channel
->prev
;
1568 if(channel
->suspended
)
1570 struct chanNode
*cNode
= channel
->channel
;
1571 struct suspended
*suspended
, *next_suspended
;
1573 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1575 next_suspended
= suspended
->previous
;
1576 free(suspended
->suspender
);
1577 free(suspended
->reason
);
1578 if(suspended
->expires
)
1579 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1584 cNode
->channel_info
= NULL
;
1586 channel
->channel
->channel_info
= NULL
;
1588 dict_delete(channel
->notes
);
1589 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1590 if(!IsSuspended(channel
))
1591 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1592 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1593 UnlockChannel(channel
->channel
);
1595 registered_channels
--;
1599 expire_channels(UNUSED_ARG(void *data
))
1601 struct chanData
*channel
, *next
;
1602 struct userData
*user
;
1603 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1605 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1606 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1608 for(channel
= channelList
; channel
; channel
= next
)
1610 next
= channel
->next
;
1612 /* See if the channel can be expired. */
1613 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1614 || IsProtected(channel
))
1617 /* Make sure there are no high-ranking users still in the channel. */
1618 for(user
=channel
->users
; user
; user
=user
->next
)
1619 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1624 /* Unregister the channel */
1625 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1626 spamserv_cs_unregister(NULL
, channel
->channel
, expire
, NULL
);
1627 unregister_channel(channel
, "registration expired.");
1630 if(chanserv_conf
.channel_expire_frequency
)
1631 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1635 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1637 char protect
= channel
->chOpts
[chProtect
];
1638 struct userData
*cs_victim
, *cs_aggressor
;
1640 /* Don't protect if no one is to be protected, someone is attacking
1641 himself, or if the aggressor is an IRC Operator. */
1642 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1645 /* Don't protect if the victim isn't authenticated (because they
1646 can't be a channel user), unless we are to protect non-users
1648 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1649 if(protect
!= 'a' && !cs_victim
)
1652 /* Protect if the aggressor isn't a user because at this point,
1653 the aggressor can only be less than or equal to the victim. */
1654 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1658 /* If the aggressor was a user, then the victim can't be helped. */
1665 if(cs_victim
->access
> cs_aggressor
->access
)
1670 if(cs_victim
->access
>= cs_aggressor
->access
)
1679 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1681 struct chanData
*cData
= channel
->channel_info
;
1682 struct userData
*cs_victim
;
1684 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1685 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1686 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1688 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1696 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1698 struct chanData
*cData
= channel
->channel_info
;
1699 struct userData
*cs_victim
;
1701 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1702 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1703 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1705 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1714 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1716 if(IsService(victim
))
1718 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1722 if(protect_user(victim
, user
, channel
->channel_info
))
1724 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1732 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1734 if(IsService(victim
))
1736 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1740 if(protect_user(victim
, user
, channel
->channel_info
))
1742 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1749 static struct do_not_register
*
1750 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1752 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1753 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1754 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1755 strcpy(dnr
->reason
, reason
);
1757 if(dnr
->chan_name
[0] == '*')
1758 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1759 else if(strpbrk(dnr
->chan_name
, "*?"))
1760 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1762 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1766 static struct dnrList
1767 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1769 struct dnrList list
;
1771 struct do_not_register
*dnr
;
1773 dnrList_init(&list
);
1774 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1775 dnrList_append(&list
, dnr
);
1776 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1777 dnrList_append(&list
, dnr
);
1779 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1780 if(match_ircglob(chan_name
, iter_key(it
)))
1781 dnrList_append(&list
, iter_data(it
));
1786 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1788 struct dnrList list
;
1789 struct do_not_register
*dnr
;
1791 char buf
[INTERVALLEN
];
1793 list
= chanserv_find_dnrs(chan_name
, handle
);
1794 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1796 dnr
= list
.list
[ii
];
1799 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1800 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1803 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1806 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1811 struct do_not_register
*
1812 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1814 struct do_not_register
*dnr
;
1817 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1821 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1823 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1824 if(match_ircglob(chan_name
, iter_key(it
)))
1825 return iter_data(it
);
1830 static CHANSERV_FUNC(cmd_noregister
)
1833 struct do_not_register
*dnr
;
1834 char buf
[INTERVALLEN
];
1835 unsigned int matches
;
1841 reply("CSMSG_DNR_SEARCH_RESULTS");
1842 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1845 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1847 dnr
= iter_data(it
);
1849 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1851 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1854 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1856 dnr
= iter_data(it
);
1858 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1860 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1863 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1865 dnr
= iter_data(it
);
1867 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1869 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1874 reply("MSG_MATCH_COUNT", matches
);
1876 reply("MSG_NO_MATCHES");
1882 if(!IsChannelName(target
) && (*target
!= '*'))
1884 reply("CSMSG_NOT_DNR", target
);
1890 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1891 if((*target
== '*') && !get_handle_info(target
+ 1))
1893 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1896 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1897 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1901 reply("CSMSG_DNR_SEARCH_RESULTS");
1902 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1905 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1907 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1909 reply("MSG_NO_MATCHES");
1913 static CHANSERV_FUNC(cmd_allowregister
)
1915 const char *chan_name
= argv
[1];
1917 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1919 dict_remove(handle_dnrs
, chan_name
+1);
1920 reply("CSMSG_DNR_REMOVED", chan_name
);
1922 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1924 dict_remove(plain_dnrs
, chan_name
);
1925 reply("CSMSG_DNR_REMOVED", chan_name
);
1927 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1929 dict_remove(mask_dnrs
, chan_name
);
1930 reply("CSMSG_DNR_REMOVED", chan_name
);
1934 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1941 chanserv_get_owned_count(struct handle_info
*hi
)
1943 struct userData
*cList
;
1946 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1947 if(cList
->access
== UL_OWNER
)
1952 static CHANSERV_FUNC(cmd_register
)
1954 struct handle_info
*handle
;
1955 struct chanData
*cData
;
1956 struct modeNode
*mn
;
1957 char reason
[MAXLEN
];
1959 unsigned int new_channel
, force
=0;
1960 struct do_not_register
*dnr
;
1966 if(channel
->channel_info
)
1968 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1972 if(channel
->bad_channel
)
1974 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1978 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1980 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1985 chan_name
= channel
->name
;
1991 reply("MSG_MISSING_PARAMS", cmd
->name
);
1992 svccmd_send_help_brief(user
, chanserv
, cmd
);
1995 if(!IsChannelName(argv
[1]))
1997 reply("MSG_NOT_CHANNEL_NAME");
2001 if(opserv_bad_channel(argv
[1]))
2003 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2008 chan_name
= argv
[1];
2011 if(argc
>= (new_channel
+2))
2013 if(!IsHelping(user
))
2015 reply("CSMSG_PROXY_FORBIDDEN");
2019 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
2021 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
2022 dnr
= chanserv_is_dnr(chan_name
, handle
);
2024 /* Check if they are over the limit.. */
2025 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2027 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2034 handle
= user
->handle_info
;
2035 dnr
= chanserv_is_dnr(chan_name
, handle
);
2036 /* Check if they are over the limit.. */
2037 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2039 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2042 /* Check if another service is in the channel */
2044 for(n
= 0; n
< channel
->members
.used
; n
++)
2046 mn
= channel
->members
.list
[n
];
2047 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2049 reply("CSMSG_ANOTHER_SERVICE");
2056 if(!IsHelping(user
))
2057 reply("CSMSG_DNR_CHANNEL", chan_name
);
2059 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2063 /* now handled above for message specilization *
2064 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2066 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2072 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2074 cData
= register_channel(channel
, user
->handle_info
->handle
);
2075 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2076 cData
->modes
= chanserv_conf
.default_modes
;
2078 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2079 if (IsOffChannel(cData
))
2081 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2085 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2086 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2087 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2089 mod_chanmode_announce(chanserv
, channel
, change
);
2090 mod_chanmode_free(change
);
2093 /* Initialize the channel's max user record. */
2094 cData
->max
= channel
->members
.used
;
2096 if(handle
!= user
->handle_info
)
2097 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2099 reply("CSMSG_REG_SUCCESS", channel
->name
);
2101 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2102 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2107 make_confirmation_string(struct userData
*uData
)
2109 static char strbuf
[16];
2114 for(src
= uData
->handle
->handle
; *src
; )
2115 accum
= accum
* 31 + toupper(*src
++);
2117 for(src
= uData
->channel
->channel
->name
; *src
; )
2118 accum
= accum
* 31 + toupper(*src
++);
2119 sprintf(strbuf
, "%08x", accum
);
2123 static CHANSERV_FUNC(cmd_unregister
)
2126 char reason
[MAXLEN
];
2127 struct chanData
*cData
;
2128 struct userData
*uData
;
2130 cData
= channel
->channel_info
;
2133 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2137 uData
= GetChannelUser(cData
, user
->handle_info
);
2138 if(!uData
|| (uData
->access
< UL_OWNER
))
2140 reply("CSMSG_NO_ACCESS");
2144 if(IsProtected(cData
))
2146 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2150 if(!IsHelping(user
))
2152 const char *confirm_string
;
2153 if(IsSuspended(cData
))
2155 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2158 confirm_string
= make_confirmation_string(uData
);
2159 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2161 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2166 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2167 name
= strdup(channel
->name
);
2168 unregister_channel(cData
, reason
);
2169 spamserv_cs_unregister(user
, channel
, manually
, "unregistered");
2170 reply("CSMSG_UNREG_SUCCESS", name
);
2176 ss_cs_join_channel(struct chanNode
*channel
, int spamserv_join
)
2178 extern struct userNode
*spamserv
;
2179 struct mod_chanmode
*change
;
2181 if(spamserv
&& spamserv_join
&& get_chanInfo(channel
->name
))
2183 change
= mod_chanmode_alloc(2);
2185 change
->args
[0].mode
= MODE_CHANOP
;
2186 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2187 change
->args
[1].mode
= MODE_CHANOP
;
2188 change
->args
[1].u
.member
= AddChannelUser(spamserv
, channel
);
2192 change
= mod_chanmode_alloc(1);
2194 change
->args
[0].mode
= MODE_CHANOP
;
2195 change
->args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
2198 mod_chanmode_announce(chanserv
, channel
, change
);
2199 mod_chanmode_free(change
);
2202 static CHANSERV_FUNC(cmd_move
)
2204 struct mod_chanmode change
;
2205 struct chanNode
*target
;
2206 struct modeNode
*mn
;
2207 struct userData
*uData
;
2208 char reason
[MAXLEN
];
2209 struct do_not_register
*dnr
;
2210 int chanserv_join
= 0, spamserv_join
;
2214 if(IsProtected(channel
->channel_info
))
2216 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2220 if(!IsChannelName(argv
[1]))
2222 reply("MSG_NOT_CHANNEL_NAME");
2226 if(opserv_bad_channel(argv
[1]))
2228 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2232 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2234 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2236 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2238 if(!IsHelping(user
))
2239 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2241 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2247 mod_chanmode_init(&change
);
2248 if(!(target
= GetChannel(argv
[1])))
2250 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2251 if(!IsSuspended(channel
->channel_info
))
2254 else if(target
->channel_info
)
2256 reply("CSMSG_ALREADY_REGGED", target
->name
);
2259 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2260 && !IsHelping(user
))
2262 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2265 else if(!IsSuspended(channel
->channel_info
))
2270 /* Clear MODE_REGISTERED from old channel, add it to new. */
2272 change
.modes_clear
= MODE_REGISTERED
;
2273 mod_chanmode_announce(chanserv
, channel
, &change
);
2274 change
.modes_clear
= 0;
2275 change
.modes_set
= MODE_REGISTERED
;
2276 mod_chanmode_announce(chanserv
, target
, &change
);
2279 /* Move the channel_info to the target channel; it
2280 shouldn't be necessary to clear timeq callbacks
2281 for the old channel. */
2282 target
->channel_info
= channel
->channel_info
;
2283 target
->channel_info
->channel
= target
;
2284 channel
->channel_info
= NULL
;
2286 spamserv_join
= spamserv_cs_move_merge(user
, channel
, target
, 1);
2289 ss_cs_join_channel(target
, spamserv_join
);
2291 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2292 if(!IsSuspended(target
->channel_info
))
2294 char reason2
[MAXLEN
];
2295 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2296 DelChannelUser(chanserv
, channel
, reason2
, 0);
2298 UnlockChannel(channel
);
2299 LockChannel(target
);
2300 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2301 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2306 merge_users(struct chanData
*source
, struct chanData
*target
)
2308 struct userData
*suData
, *tuData
, *next
;
2314 /* Insert the source's users into the scratch area. */
2315 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2316 dict_insert(merge
, suData
->handle
->handle
, suData
);
2318 /* Iterate through the target's users, looking for
2319 users common to both channels. The lower access is
2320 removed from either the scratch area or target user
2322 for(tuData
= target
->users
; tuData
; tuData
= next
)
2324 struct userData
*choice
;
2326 next
= tuData
->next
;
2328 /* If a source user exists with the same handle as a target
2329 channel's user, resolve the conflict by removing one. */
2330 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2334 /* Pick the data we want to keep. */
2335 /* If the access is the same, use the later seen time. */
2336 if(suData
->access
== tuData
->access
)
2337 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2338 else /* Otherwise, keep the higher access level. */
2339 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2341 /* Remove the user that wasn't picked. */
2342 if(choice
== tuData
)
2344 dict_remove(merge
, suData
->handle
->handle
);
2345 del_channel_user(suData
, 0);
2348 del_channel_user(tuData
, 0);
2351 /* Move the remaining users to the target channel. */
2352 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2354 suData
= iter_data(it
);
2356 /* Insert the user into the target channel's linked list. */
2357 suData
->prev
= NULL
;
2358 suData
->next
= target
->users
;
2359 suData
->channel
= target
;
2362 target
->users
->prev
= suData
;
2363 target
->users
= suData
;
2365 /* Update the user counts for the target channel; the
2366 source counts are left alone. */
2367 target
->userCount
++;
2370 /* Possible to assert (source->users == NULL) here. */
2371 source
->users
= NULL
;
2376 merge_bans(struct chanData
*source
, struct chanData
*target
)
2378 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2380 /* Hold on to the original head of the target ban list
2381 to avoid comparing source bans with source bans. */
2382 tFront
= target
->bans
;
2384 /* Perform a totally expensive O(n*m) merge, ick. */
2385 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2387 /* Flag to track whether the ban's been moved
2388 to the destination yet. */
2391 /* Possible to assert (sbData->prev == NULL) here. */
2392 sNext
= sbData
->next
;
2394 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2396 tNext
= tbData
->next
;
2398 /* Perform two comparisons between each source
2399 and target ban, conflicts are resolved by
2400 keeping the broader ban and copying the later
2401 expiration and triggered time. */
2402 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2404 /* There is a broader ban in the target channel that
2405 overrides one in the source channel; remove the
2406 source ban and break. */
2407 if(sbData
->expires
> tbData
->expires
)
2408 tbData
->expires
= sbData
->expires
;
2409 if(sbData
->triggered
> tbData
->triggered
)
2410 tbData
->triggered
= sbData
->triggered
;
2411 del_channel_ban(sbData
);
2414 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2416 /* There is a broader ban in the source channel that
2417 overrides one in the target channel; remove the
2418 target ban, fall through and move the source over. */
2419 if(tbData
->expires
> sbData
->expires
)
2420 sbData
->expires
= tbData
->expires
;
2421 if(tbData
->triggered
> sbData
->triggered
)
2422 sbData
->triggered
= tbData
->triggered
;
2423 if(tbData
== tFront
)
2425 del_channel_ban(tbData
);
2428 /* Source bans can override multiple target bans, so
2429 we allow a source to run through this loop multiple
2430 times, but we can only move it once. */
2435 /* Remove the source ban from the source ban list. */
2437 sbData
->next
->prev
= sbData
->prev
;
2439 /* Modify the source ban's associated channel. */
2440 sbData
->channel
= target
;
2442 /* Insert the ban into the target channel's linked list. */
2443 sbData
->prev
= NULL
;
2444 sbData
->next
= target
->bans
;
2447 target
->bans
->prev
= sbData
;
2448 target
->bans
= sbData
;
2450 /* Update the user counts for the target channel. */
2455 /* Possible to assert (source->bans == NULL) here. */
2456 source
->bans
= NULL
;
2460 merge_data(struct chanData
*source
, struct chanData
*target
)
2462 /* Use more recent visited and owner-transfer time; use older
2463 * registered time. Bitwise or may_opchan. Use higher max.
2464 * Do not touch last_refresh, ban count or user counts.
2466 if(source
->visited
> target
->visited
)
2467 target
->visited
= source
->visited
;
2468 if(source
->registered
< target
->registered
)
2469 target
->registered
= source
->registered
;
2470 if(source
->ownerTransfer
> target
->ownerTransfer
)
2471 target
->ownerTransfer
= source
->ownerTransfer
;
2472 if(source
->may_opchan
)
2473 target
->may_opchan
= 1;
2474 if(source
->max
> target
->max
)
2475 target
->max
= source
->max
;
2479 merge_channel(struct chanData
*source
, struct chanData
*target
)
2481 merge_users(source
, target
);
2482 merge_bans(source
, target
);
2483 merge_data(source
, target
);
2486 static CHANSERV_FUNC(cmd_merge
)
2488 struct userData
*target_user
;
2489 struct chanNode
*target
;
2490 char reason
[MAXLEN
];
2494 /* Make sure the target channel exists and is registered to the user
2495 performing the command. */
2496 if(!(target
= GetChannel(argv
[1])))
2498 reply("MSG_INVALID_CHANNEL");
2502 if(!target
->channel_info
)
2504 reply("CSMSG_NOT_REGISTERED", target
->name
);
2508 if(IsProtected(channel
->channel_info
))
2510 reply("CSMSG_MERGE_NODELETE");
2514 if(IsSuspended(target
->channel_info
))
2516 reply("CSMSG_MERGE_SUSPENDED");
2520 if(channel
== target
)
2522 reply("CSMSG_MERGE_SELF");
2526 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2527 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2529 reply("CSMSG_MERGE_NOT_OWNER");
2533 /* Merge the channel structures and associated data. */
2534 merge_channel(channel
->channel_info
, target
->channel_info
);
2535 spamserv_cs_move_merge(user
, channel
, target
, 0);
2536 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2537 unregister_channel(channel
->channel_info
, reason
);
2538 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2542 static CHANSERV_FUNC(cmd_opchan
)
2544 struct mod_chanmode change
;
2545 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2547 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2550 channel
->channel_info
->may_opchan
= 0;
2551 mod_chanmode_init(&change
);
2553 change
.args
[0].mode
= MODE_CHANOP
;
2554 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2555 mod_chanmode_announce(chanserv
, channel
, &change
);
2556 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2560 static CHANSERV_FUNC(cmd_adduser
)
2562 struct userData
*actee
;
2563 struct userData
*actor
;
2564 struct handle_info
*handle
;
2565 unsigned short access
;
2569 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2571 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2575 access
= user_level_from_name(argv
[2], UL_OWNER
);
2578 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2582 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2583 if(actor
->access
<= access
)
2585 reply("CSMSG_NO_BUMP_ACCESS");
2589 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2591 // 'kevin must first authenticate with AuthServ.' is sent to user
2592 struct userNode
*unode
;
2593 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2596 if(find_adduser_pending(channel
, unode
)) {
2597 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2600 if(IsInChannel(channel
, unode
)) {
2601 reply("CSMSG_ADDUSER_PENDING");
2602 add_adduser_pending(channel
, unode
, access
);
2603 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2605 /* this results in user must auth AND not in chan errors. too confusing..
2607 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2615 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2617 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2621 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2622 scan_user_presence(actee
, NULL
);
2623 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2627 static CHANSERV_FUNC(cmd_clvl
)
2629 struct handle_info
*handle
;
2630 struct userData
*victim
;
2631 struct userData
*actor
;
2632 unsigned short new_access
;
2633 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2637 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2639 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2642 if(handle
== user
->handle_info
&& !privileged
)
2644 reply("CSMSG_NO_SELF_CLVL");
2648 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2650 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2654 if(actor
->access
<= victim
->access
&& !privileged
)
2656 reply("MSG_USER_OUTRANKED", handle
->handle
);
2660 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2664 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2668 if(new_access
>= actor
->access
&& !privileged
)
2670 reply("CSMSG_NO_BUMP_ACCESS");
2674 victim
->access
= new_access
;
2675 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2679 static CHANSERV_FUNC(cmd_deluser
)
2681 struct handle_info
*handle
;
2682 struct userData
*victim
;
2683 struct userData
*actor
;
2684 unsigned short access
;
2689 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2691 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2694 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2696 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2702 access
= user_level_from_name(argv
[1], UL_OWNER
);
2705 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2708 if(access
!= victim
->access
)
2710 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2716 access
= victim
->access
;
2719 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2721 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2725 chan_name
= strdup(channel
->name
);
2726 del_channel_user(victim
, 1);
2727 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2733 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2735 struct userData
*actor
, *uData
, *next
;
2737 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2739 if(min_access
> max_access
)
2741 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2745 if((actor
->access
<= max_access
) && !IsHelping(user
))
2747 reply("CSMSG_NO_ACCESS");
2751 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2755 if((uData
->access
>= min_access
)
2756 && (uData
->access
<= max_access
)
2757 && match_ircglob(uData
->handle
->handle
, mask
))
2758 del_channel_user(uData
, 1);
2761 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2765 static CHANSERV_FUNC(cmd_mdelowner
)
2767 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2770 static CHANSERV_FUNC(cmd_mdelcoowner
)
2772 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2775 static CHANSERV_FUNC(cmd_mdelmanager
)
2777 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2780 static CHANSERV_FUNC(cmd_mdelop
)
2782 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2785 static CHANSERV_FUNC(cmd_mdelpeon
)
2787 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2790 static CHANSERV_FUNC(cmd_mdelhalfop
)
2792 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2798 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2800 struct banData
*bData
, *next
;
2801 char interval
[INTERVALLEN
];
2806 limit
= now
- duration
;
2807 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2811 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2814 del_channel_ban(bData
);
2818 intervalString(interval
, duration
, user
->handle_info
);
2819 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2824 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2826 struct userData
*actor
, *uData
, *next
;
2827 char interval
[INTERVALLEN
];
2831 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2832 if(min_access
> max_access
)
2834 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2838 if((actor
->access
<= max_access
) && !IsHelping(user
))
2840 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2845 limit
= now
- duration
;
2846 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2850 if((uData
->seen
> limit
)
2852 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2855 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2856 || (!max_access
&& (uData
->access
< actor
->access
)))
2858 del_channel_user(uData
, 1);
2866 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2868 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2872 static CHANSERV_FUNC(cmd_trim
)
2874 unsigned long duration
;
2875 unsigned short min_level
, max_level
;
2880 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2881 duration
= ParseInterval(argv
[2]);
2884 reply("CSMSG_CANNOT_TRIM");
2888 if(!irccasecmp(argv
[1], "lamers"))
2890 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2893 else if(!irccasecmp(argv
[1], "users"))
2895 cmd_trim_users(user
, channel
, 0, 0, duration
, vacation
);
2898 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2900 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
, vacation
);
2903 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2905 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
, vacation
);
2910 reply("CSMSG_INVALID_TRIM", argv
[1]);
2915 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2916 to the user. cmd_all takes advantage of this. */
2917 static CHANSERV_FUNC(cmd_up
)
2919 struct mod_chanmode change
;
2920 struct userData
*uData
;
2923 mod_chanmode_init(&change
);
2925 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2926 if(!change
.args
[0].u
.member
)
2929 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2933 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2937 reply("CSMSG_GODMODE_UP", argv
[0]);
2940 else if(uData
->access
>= UL_OP
)
2942 change
.args
[0].mode
= MODE_CHANOP
;
2943 errmsg
= "CSMSG_ALREADY_OPPED";
2945 else if(uData
->access
>= UL_HALFOP
)
2947 change
.args
[0].mode
= MODE_HALFOP
;
2948 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2950 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2952 change
.args
[0].mode
= MODE_VOICE
;
2953 errmsg
= "CSMSG_ALREADY_VOICED";
2958 reply("CSMSG_NO_ACCESS");
2961 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2962 if(!change
.args
[0].mode
)
2965 reply(errmsg
, channel
->name
);
2968 modcmd_chanmode_announce(&change
);
2972 static CHANSERV_FUNC(cmd_down
)
2974 struct mod_chanmode change
;
2976 mod_chanmode_init(&change
);
2978 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2979 if(!change
.args
[0].u
.member
)
2982 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2986 if(!change
.args
[0].u
.member
->modes
)
2989 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2993 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2994 modcmd_chanmode_announce(&change
);
2998 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
)
3000 struct userData
*cList
;
3002 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
3004 if(IsSuspended(cList
->channel
)
3005 || IsUserSuspended(cList
)
3006 || !GetUserMode(cList
->channel
->channel
, user
))
3009 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
3015 static CHANSERV_FUNC(cmd_upall
)
3017 return cmd_all(CSFUNC_ARGS
, cmd_up
);
3020 static CHANSERV_FUNC(cmd_downall
)
3022 return cmd_all(CSFUNC_ARGS
, cmd_down
);
3025 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
3026 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
3029 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
)
3031 unsigned int ii
, valid
;
3032 struct userNode
*victim
;
3033 struct mod_chanmode
*change
;
3035 change
= mod_chanmode_alloc(argc
- 1);
3037 for(ii
=valid
=0; ++ii
< argc
; )
3039 if(!(victim
= GetUserH(argv
[ii
])))
3041 change
->args
[valid
].mode
= mode
;
3042 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
3043 if(!change
->args
[valid
].u
.member
)
3045 if(validate
&& !validate(user
, channel
, victim
))
3050 change
->argc
= valid
;
3051 if(valid
< (argc
-1))
3052 reply("CSMSG_PROCESS_FAILED");
3055 modcmd_chanmode_announce(change
);
3056 reply(action
, channel
->name
);
3058 mod_chanmode_free(change
);
3062 static CHANSERV_FUNC(cmd_op
)
3064 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3067 static CHANSERV_FUNC(cmd_hop
)
3069 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3072 static CHANSERV_FUNC(cmd_deop
)
3074 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3077 static CHANSERV_FUNC(cmd_dehop
)
3079 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3082 static CHANSERV_FUNC(cmd_voice
)
3084 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3087 static CHANSERV_FUNC(cmd_devoice
)
3089 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3093 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3099 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3101 struct modeNode
*mn
= channel
->members
.list
[ii
];
3103 if(IsService(mn
->user
))
3106 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3109 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3113 victims
[(*victimCount
)++] = mn
;
3119 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3121 struct userNode
*victim
;
3122 struct modeNode
**victims
;
3123 unsigned int offset
, n
, victimCount
, duration
= 0;
3124 char *reason
= "Bye.", *ban
, *name
;
3125 char interval
[INTERVALLEN
];
3127 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3128 REQUIRE_PARAMS(offset
);
3131 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3132 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3134 /* Truncate the reason to a length of TOPICLEN, as
3135 the ircd does; however, leave room for an ellipsis
3136 and the kicker's nick. */
3137 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3141 if((victim
= GetUserH(argv
[1])))
3143 victims
= alloca(sizeof(victims
[0]));
3144 victims
[0] = GetUserMode(channel
, victim
);
3145 /* XXX: The comparison with ACTION_KICK is just because all
3146 * other actions can work on users outside the channel, and we
3147 * want to allow those (e.g. unbans) in that case. If we add
3148 * some other ejection action for in-channel users, change
3150 victimCount
= victims
[0] ? 1 : 0;
3152 if(IsService(victim
))
3155 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3159 if((action
== ACTION_KICK
) && !victimCount
)
3162 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3166 if(protect_user(victim
, user
, channel
->channel_info
))
3168 // This translates to send_message(user, cmd->parent->bot, ...)
3169 // if user is x3 (ctcp action) cmd is null and segfault.
3171 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3175 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3176 name
= victim
->nick
;
3180 if(!is_ircmask(argv
[1]))
3183 reply("MSG_NICK_UNKNOWN", argv
[1]);
3187 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3189 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3192 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3195 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3196 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3198 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3199 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3200 some creativity, but its not x3's job to be the ban censor anyway. */
3201 if(is_overmask(argv
[1]))
3204 reply("CSMSG_LAME_MASK", argv
[1]);
3208 if((action
== ACTION_KICK
) && (victimCount
== 0))
3211 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3215 name
= ban
= strdup(argv
[1]);
3218 /* Truncate the ban in place if necessary; we must ensure
3219 that 'ban' is a valid ban mask before sanitizing it. */
3220 sanitize_ircmask(ban
);
3222 if(action
& ACTION_ADD_LAMER
)
3224 struct banData
*bData
, *next
;
3226 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3229 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3234 if(action
& ACTION_ADD_TIMED_LAMER
)
3236 duration
= ParseInterval(argv
[2]);
3241 reply("CSMSG_DURATION_TOO_LOW");
3245 else if(duration
> (86400 * 365 * 2))
3248 reply("CSMSG_DURATION_TOO_HIGH");
3255 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3257 if(match_ircglobs(bData
->mask
, ban
))
3259 int exact
= !irccasecmp(bData
->mask
, ban
);
3261 /* The ban is redundant; there is already a ban
3262 with the same effect in place. */
3266 free(bData
->reason
);
3267 bData
->reason
= strdup(reason
);
3268 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3270 reply("CSMSG_REASON_CHANGE", ban
);
3274 if(exact
&& bData
->expires
)
3278 /* If the ban matches an existing one exactly,
3279 extend the expiration time if the provided
3280 duration is longer. */
3281 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3283 bData
->expires
= now
+ duration
;
3294 /* Delete the expiration timeq entry and
3295 requeue if necessary. */
3296 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3299 timeq_add(bData
->expires
, expire_ban
, bData
);
3303 /* automated kickban, dont reply */
3306 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3308 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3314 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3321 if(match_ircglobs(ban
, bData
->mask
))
3323 /* The ban we are adding makes previously existing
3324 bans redundant; silently remove them. */
3325 del_channel_ban(bData
);
3329 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
);
3331 name
= ban
= strdup(bData
->mask
);
3335 /* WHAT DOES THIS DO?? -Rubin */
3336 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3338 extern const char *hidden_host_suffix
;
3339 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3341 unsigned int l1
, l2
;
3344 l2
= strlen(old_name
);
3347 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3349 new_mask
= malloc(MAXLEN
);
3350 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3352 name
= ban
= new_mask
;
3357 if(action
& ACTION_BAN
)
3359 unsigned int exists
;
3360 struct mod_chanmode
*change
;
3362 if(channel
->banlist
.used
>= MAXBANS
)
3365 reply("CSMSG_BANLIST_FULL", channel
->name
);
3370 exists
= ChannelBanExists(channel
, ban
);
3371 change
= mod_chanmode_alloc(victimCount
+ 1);
3372 for(n
= 0; n
< victimCount
; ++n
)
3374 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3375 change
->args
[n
].u
.member
= victims
[n
];
3379 change
->args
[n
].mode
= MODE_BAN
;
3380 change
->args
[n
++].u
.hostmask
= ban
;
3384 modcmd_chanmode_announce(change
);
3386 mod_chanmode_announce(chanserv
, channel
, change
);
3387 mod_chanmode_free(change
);
3389 if(exists
&& (action
== ACTION_BAN
))
3392 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3398 if(action
& ACTION_KICK
)
3400 char kick_reason
[MAXLEN
];
3401 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3403 for(n
= 0; n
< victimCount
; n
++)
3404 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3409 /* No response, since it was automated. */
3411 else if(action
& ACTION_ADD_LAMER
)
3414 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3416 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3418 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3419 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3420 else if(action
& ACTION_BAN
)
3421 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3422 else if(action
& ACTION_KICK
&& victimCount
)
3423 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3429 static CHANSERV_FUNC(cmd_kickban
)
3431 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3434 static CHANSERV_FUNC(cmd_kick
)
3436 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3439 static CHANSERV_FUNC(cmd_ban
)
3441 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3444 static CHANSERV_FUNC(cmd_addlamer
)
3446 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3449 static CHANSERV_FUNC(cmd_addtimedlamer
)
3451 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3454 static struct mod_chanmode
*
3455 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3457 struct mod_chanmode
*change
;
3458 unsigned char *match
;
3459 unsigned int ii
, count
;
3461 match
= alloca(bans
->used
);
3464 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3466 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3467 MATCH_USENICK
| MATCH_VISIBLE
);
3474 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3476 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3483 change
= mod_chanmode_alloc(count
);
3484 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3488 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3489 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3491 assert(count
== change
->argc
);
3495 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3497 unsigned int jj
, ii
, count
;
3499 struct chanData
*channel
;
3501 struct mod_chanmode
*change
;
3503 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3504 /* Walk through every channel */
3505 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3506 switch(channel
->chOpts
[chBanTimeout
])
3508 default: case '0': continue; /* Dont remove bans in this chan */
3509 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3510 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3511 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3512 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3513 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3516 /* First find out how many bans were going to unset */
3517 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3518 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3522 /* At least one ban, so setup a removal */
3523 change
= mod_chanmode_alloc(count
);
3525 /* Walk over every ban in this channel.. */
3526 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3527 bn
= channel
->channel
->banlist
.list
[jj
];
3528 if (bn
->set
< bantimeout
) {
3529 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3531 /* Add this ban to the mode change */
3532 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3533 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3535 /* Pull this ban out of the list */
3536 banList_remove(&(channel
->channel
->banlist
), bn
);
3541 /* Send the modes to IRC */
3542 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3544 /* free memory from strdup above */
3545 for(ii
= 0; ii
< count
; ++ii
)
3546 free((char*)change
->args
[ii
].u
.hostmask
);
3548 mod_chanmode_free(change
);
3551 /* Set this function to run again */
3552 if(chanserv_conf
.ban_timeout_frequency
)
3553 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3558 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3560 struct userNode
*actee
;
3566 /* may want to allow a comma delimited list of users... */
3567 if(!(actee
= GetUserH(argv
[1])))
3569 if(!is_ircmask(argv
[1]))
3571 reply("MSG_NICK_UNKNOWN", argv
[1]);
3575 mask
= strdup(argv
[1]);
3578 /* We don't sanitize the mask here because ircu
3580 if(action
& ACTION_UNBAN
)
3582 struct mod_chanmode
*change
;
3583 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3588 modcmd_chanmode_announce(change
);
3589 for(ii
= 0; ii
< change
->argc
; ++ii
)
3590 free((char*)change
->args
[ii
].u
.hostmask
);
3591 mod_chanmode_free(change
);
3596 if(action
& ACTION_DEL_LAMER
)
3598 struct banData
*ban
, *next
;
3600 ban
= channel
->channel_info
->bans
; /* lamers */
3604 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3607 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3612 del_channel_ban(ban
);
3619 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3621 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3627 static CHANSERV_FUNC(cmd_unban
)
3629 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3632 static CHANSERV_FUNC(cmd_dellamer
)
3634 /* it doesn't necessarily have to remove the channel ban - may want
3635 to make that an option. */
3636 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3639 static CHANSERV_FUNC(cmd_unbanme
)
3641 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3642 long flags
= ACTION_UNBAN
;
3644 /* remove permanent bans if the user has the proper access. */
3645 if(uData
->access
>= UL_MANAGER
)
3646 flags
|= ACTION_DEL_LAMER
;
3648 argv
[1] = user
->nick
;
3649 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3652 static CHANSERV_FUNC(cmd_unbanall
)
3654 struct mod_chanmode
*change
;
3657 if(!channel
->banlist
.used
)
3659 reply("CSMSG_NO_BANS", channel
->name
);
3663 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3664 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3666 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3667 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3669 modcmd_chanmode_announce(change
);
3670 for(ii
= 0; ii
< change
->argc
; ++ii
)
3671 free((char*)change
->args
[ii
].u
.hostmask
);
3672 mod_chanmode_free(change
);
3673 reply("CSMSG_BANS_REMOVED", channel
->name
);
3677 static CHANSERV_FUNC(cmd_open
)
3679 struct mod_chanmode
*change
;
3682 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3684 change
= mod_chanmode_alloc(0);
3685 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3686 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3687 && channel
->channel_info
->modes
.modes_set
)
3688 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3689 modcmd_chanmode_announce(change
);
3690 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3691 for(ii
= 0; ii
< change
->argc
; ++ii
)
3692 free((char*)change
->args
[ii
].u
.hostmask
);
3693 mod_chanmode_free(change
);
3697 static CHANSERV_FUNC(cmd_myaccess
)
3699 static struct string_buffer sbuf
;
3700 struct handle_info
*target_handle
;
3701 struct userData
*uData
;
3704 target_handle
= user
->handle_info
;
3705 else if(!IsHelping(user
))
3707 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3710 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3713 if(!target_handle
->channels
)
3715 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3719 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3720 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3722 struct chanData
*cData
= uData
->channel
;
3724 if(uData
->access
> UL_OWNER
)
3726 if(IsProtected(cData
)
3727 && (target_handle
!= user
->handle_info
)
3728 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3731 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3732 if(uData
->flags
== USER_AUTO_OP
)
3733 string_buffer_append(&sbuf
, ',');
3734 if(IsUserSuspended(uData
))
3735 string_buffer_append(&sbuf
, 's');
3736 if(IsUserAutoOp(uData
))
3738 if(uData
->access
>= UL_OP
)
3739 string_buffer_append(&sbuf
, 'o');
3740 else if(uData
->access
>= UL_HALFOP
)
3741 string_buffer_append(&sbuf
, 'h');
3742 else if(uData
->access
>= UL_PEON
)
3743 string_buffer_append(&sbuf
, 'v');
3745 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3746 string_buffer_append(&sbuf
, 'i');
3748 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3750 string_buffer_append_string(&sbuf
, ")]");
3751 string_buffer_append(&sbuf
, '\0');
3752 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3758 static CHANSERV_FUNC(cmd_access
)
3760 struct userNode
*target
;
3761 struct handle_info
*target_handle
;
3762 struct userData
*uData
;
3764 char prefix
[MAXLEN
];
3769 target_handle
= target
->handle_info
;
3771 else if((target
= GetUserH(argv
[1])))
3773 target_handle
= target
->handle_info
;
3775 else if(argv
[1][0] == '*')
3777 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3779 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3785 reply("MSG_NICK_UNKNOWN", argv
[1]);
3789 assert(target
|| target_handle
);
3791 if(target
== chanserv
)
3793 reply("CSMSG_IS_CHANSERV");
3801 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3806 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3809 reply("MSG_AUTHENTICATE");
3815 const char *epithet
= NULL
, *type
= NULL
;
3818 epithet
= chanserv_conf
.irc_operator_epithet
;
3821 else if(IsNetworkHelper(target
))
3823 epithet
= chanserv_conf
.network_helper_epithet
;
3824 type
= "network helper";
3826 else if(IsSupportHelper(target
))
3828 epithet
= chanserv_conf
.support_helper_epithet
;
3829 type
= "support helper";
3833 if(target_handle
->epithet
)
3834 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3836 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3838 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3842 sprintf(prefix
, "%s", target_handle
->handle
);
3845 if(!channel
->channel_info
)
3847 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3851 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3852 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3853 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3855 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3856 /* To prevent possible information leaks, only show infolines
3857 * if the requestor is in the channel or it's their own
3859 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3861 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3863 /* Likewise, only say it's suspended if the user has active
3864 * access in that channel or it's their own entry. */
3865 if(IsUserSuspended(uData
)
3866 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3867 || (user
->handle_info
== uData
->handle
)))
3869 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3874 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3880 /* This is never used...
3882 zoot_list(struct listData *list)
3884 struct userData *uData;
3885 unsigned int start, curr, highest, lowest;
3886 struct helpfile_table tmp_table;
3887 const char **temp, *msg;
3889 if(list->table.length == 1)
3892 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);
3894 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));
3895 msg = user_find_message(list->user, "MSG_NONE");
3896 send_message_type(4, list->user, list->bot, " %s", msg);
3898 tmp_table.width = list->table.width;
3899 tmp_table.flags = list->table.flags;
3900 list->table.contents[0][0] = " ";
3901 highest = list->highest;
3902 if(list->lowest != 0)
3903 lowest = list->lowest;
3904 else if(highest < 100)
3907 lowest = highest - 100;
3908 for(start = curr = 1; curr < list->table.length; )
3910 uData = list->users[curr-1];
3911 list->table.contents[curr++][0] = " ";
3912 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3915 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);
3917 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));
3918 temp = list->table.contents[--start];
3919 list->table.contents[start] = list->table.contents[0];
3920 tmp_table.contents = list->table.contents + start;
3921 tmp_table.length = curr - start;
3922 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3923 list->table.contents[start] = temp;
3925 highest = lowest - 1;
3926 lowest = (highest < 100) ? 0 : (highest - 99);
3933 normal_list(struct listData
*list
)
3937 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
);
3939 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
));
3940 if(list
->table
.length
== 1)
3942 msg
= user_find_message(list
->user
, "MSG_NONE");
3943 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3946 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3949 /* if these need changed, uncomment and customize
3951 clean_list(struct listData *list)
3955 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);
3957 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));
3958 if(list->table.length == 1)
3960 msg = user_find_message(list->user, "MSG_NONE");
3961 send_message_type(4, list->user, list->bot, " %s", msg);
3964 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3968 advanced_list(struct listData *list)
3972 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);
3974 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));
3975 if(list->table.length == 1)
3977 msg = user_find_message(list->user, "MSG_NONE");
3978 send_message_type(4, list->user, list->bot, " %s", msg);
3981 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3985 classic_list(struct listData *list)
3989 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
3991 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
3992 if(list->table.length == 1)
3994 msg = user_find_message(list->user, "MSG_NONE");
3995 send_message_type(4, list->user, list->bot, " %s", msg);
3998 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4003 userData_access_comp(const void *arg_a
, const void *arg_b
)
4005 const struct userData
*a
= *(struct userData
**)arg_a
;
4006 const struct userData
*b
= *(struct userData
**)arg_b
;
4008 if(a
->access
!= b
->access
)
4009 res
= b
->access
- a
->access
;
4011 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
4016 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
4018 void (*send_list
)(struct listData
*);
4019 struct userData
*uData
;
4020 struct listData lData
;
4021 unsigned int matches
;
4027 lData
.bot
= cmd
->parent
->bot
;
4028 lData
.channel
= channel
;
4029 lData
.lowest
= lowest
;
4030 lData
.highest
= highest
;
4031 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
4032 send_list
= normal_list
;
4033 /* What does the following line do exactly?? */
4034 /*(void)zoot_list; ** since it doesn't show user levels */
4037 if(user->handle_info)
4039 switch(user->handle_info->userlist_style)
4041 case HI_STYLE_CLEAN:
4042 send_list = clean_list;
4044 case HI_STYLE_ADVANCED:
4045 send_list = advanced_list;
4047 case HI_STYLE_CLASSIC:
4048 send_list = classic_list;
4050 case HI_STYLE_NORMAL:
4052 send_list = normal_list;
4057 send_list
= normal_list
;
4059 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4061 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4063 if((uData
->access
< lowest
)
4064 || (uData
->access
> highest
)
4065 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4067 lData
.users
[matches
++] = uData
;
4069 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4071 lData
.table
.length
= matches
+1;
4072 lData
.table
.flags
= TABLE_NO_FREE
;
4073 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4075 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4076 lData
.table
.width
= 5; /* with level = 5 */
4078 lData
.table
.width
= 4; /* without = 4 */
4079 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4080 lData
.table
.contents
[0] = ary
;
4081 if(user
->handle_info
) {
4082 switch(user
->handle_info
->userlist_style
) {
4083 case HI_STYLE_CLASSIC
:
4086 case HI_STYLE_ADVANCED
:
4087 ary
[i
++] = "Access";
4090 case HI_STYLE_CLEAN
:
4091 ary
[i
++] = "Access";
4093 case HI_STYLE_NORMAL
:
4095 ary
[i
++] = "Access";
4100 ary
[i
++] = "Access";
4102 ary
[i
++] = "Account";
4103 ary
[i
] = "Last Seen";
4105 ary
[i
++] = "Status";
4106 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4108 struct userData
*uData
= lData
.users
[matches
-1];
4109 char seen
[INTERVALLEN
];
4112 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4113 lData
.table
.contents
[matches
] = ary
;
4114 if(user
->handle_info
) {
4115 switch(user
->handle_info
->userlist_style
) {
4116 case HI_STYLE_CLASSIC
:
4117 ary
[i
++] = strtab(uData
->access
);
4119 case HI_STYLE_ADVANCED
:
4120 ary
[i
++] = user_level_name_from_level(uData
->access
);
4121 ary
[i
++] = strtab(uData
->access
);
4123 case HI_STYLE_CLEAN
:
4124 ary
[i
++] = user_level_name_from_level(uData
->access
);
4126 case HI_STYLE_NORMAL
:
4128 ary
[i
++] = user_level_name_from_level(uData
->access
);
4133 ary
[i
++] = user_level_name_from_level(uData
->access
);
4135 ary
[i
++] = uData
->handle
->handle
;
4138 else if(!uData
->seen
)
4141 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4142 ary
[i
] = strdup(ary
[i
]);
4144 if(IsUserSuspended(uData
))
4145 ary
[i
++] = "Suspended";
4146 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4147 ary
[i
++] = "Vacation";
4149 ary
[i
++] = "Normal";
4152 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4154 /* Free strdup above */
4155 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4156 free(lData
.table
.contents
[matches
]);
4158 free(lData
.table
.contents
[0]);
4159 free(lData
.table
.contents
);
4163 /* Remove this now that debugging is over? or improve it for
4164 * users? Would it be better tied into USERS somehow? -Rubin */
4165 static CHANSERV_FUNC(cmd_pending
)
4167 struct adduserPending
*ap
;
4168 reply("CSMSG_ADDUSER_PENDING_HEADER");
4169 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4171 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4172 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4173 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4177 static CHANSERV_FUNC(cmd_users
)
4179 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4182 static CHANSERV_FUNC(cmd_wlist
)
4184 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4187 static CHANSERV_FUNC(cmd_clist
)
4189 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4192 static CHANSERV_FUNC(cmd_mlist
)
4194 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4197 static CHANSERV_FUNC(cmd_olist
)
4199 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4202 static CHANSERV_FUNC(cmd_hlist
)
4204 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4207 static CHANSERV_FUNC(cmd_plist
)
4209 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4212 static CHANSERV_FUNC(cmd_lamers
)
4214 struct helpfile_table tbl
;
4215 unsigned int matches
= 0, timed
= 0, ii
;
4216 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4217 const char *msg_never
, *triggered
, *expires
;
4218 struct banData
*ban
, **bans
; /* lamers */
4225 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4226 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4229 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4231 if(search
&& !match_ircglobs(search
, ban
->mask
))
4233 bans
[matches
++] = ban
;
4238 tbl
.length
= matches
+ 1;
4239 tbl
.width
= 4 + timed
;
4241 tbl
.flags
= TABLE_NO_FREE
;
4242 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4243 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4244 tbl
.contents
[0][0] = "Mask";
4245 tbl
.contents
[0][1] = "Set By";
4246 tbl
.contents
[0][2] = "Triggered";
4249 tbl
.contents
[0][3] = "Expires";
4250 tbl
.contents
[0][4] = "Reason";
4253 tbl
.contents
[0][3] = "Reason";
4256 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4258 free(tbl
.contents
[0]);
4263 msg_never
= user_find_message(user
, "MSG_NEVER");
4264 for(ii
= 0; ii
< matches
; )
4270 else if(ban
->expires
)
4271 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4273 expires
= msg_never
;
4276 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4278 triggered
= msg_never
;
4280 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4281 tbl
.contents
[ii
][0] = ban
->mask
;
4282 tbl
.contents
[ii
][1] = ban
->owner
;
4283 tbl
.contents
[ii
][2] = strdup(triggered
);
4286 tbl
.contents
[ii
][3] = strdup(expires
);
4287 tbl
.contents
[ii
][4] = ban
->reason
;
4290 tbl
.contents
[ii
][3] = ban
->reason
;
4292 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4293 /* reply("MSG_MATCH_COUNT", matches); */
4294 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4296 free((char*)tbl
.contents
[ii
][2]);
4298 free((char*)tbl
.contents
[ii
][3]);
4299 free(tbl
.contents
[ii
]);
4301 free(tbl
.contents
[0]);
4308 * return + if the user does NOT have the right to set the topic, and
4309 * the topic is changed.
4312 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4314 struct chanData
*cData
= channel
->channel_info
;
4315 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4317 else if(cData
->topic
)
4318 return irccasecmp(new_topic
, cData
->topic
);
4325 * Makes a givin topic fit into a givin topic mask and returns
4328 * topic_mask - the mask to conform to
4329 * topic - the topic to make conform
4330 * new_topic - the pre-allocated char* to put the new topic into
4332 * modifies: new_topic
4335 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4337 //char *topic_mask = cData->topic_mask;
4339 int pos
=0, starpos
=-1, dpos
=0, len
;
4341 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4348 strcpy(new_topic
, "");
4351 len
= strlen(topic
);
4352 if((dpos
+ len
) > TOPICLEN
)
4353 len
= TOPICLEN
+ 1 - dpos
;
4354 memcpy(new_topic
+dpos
, topic
, len
);
4358 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4359 default: new_topic
[dpos
++] = tchar
; break;
4362 if((dpos
> TOPICLEN
) || tchar
)
4364 strcpy(new_topic
, "");
4367 new_topic
[dpos
] = 0;
4371 static CHANSERV_FUNC(cmd_topic
)
4373 struct chanData
*cData
;
4377 #ifdef WITH_PROTOCOL_P10
4381 cData
= channel
->channel_info
;
4386 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4387 reply("CSMSG_TOPIC_SET", cData
->topic
);
4391 reply("CSMSG_NO_TOPIC", channel
->name
);
4395 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4396 /* If they say "!topic *", use an empty topic. */
4397 if((topic
[0] == '*') && (topic
[1] == 0))
4400 if(bad_topic(channel
, user
, topic
))
4402 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4407 /* If there is a topicmask set, and the new topic doesnt match, make it */
4408 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4410 char *topic_mask
= cData
->topic_mask
;
4411 char new_topic
[TOPICLEN
+1];
4413 /* make a new topic fitting mask */
4414 conform_topic(topic_mask
, topic
, new_topic
);
4417 /* Topic couldnt fit into mask, was too long */
4418 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4419 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4422 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4424 else /* No mask set, just set the topic */
4425 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4428 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4430 /* Grab the topic and save it as the default topic. */
4432 cData
->topic
= strdup(channel
->topic
);
4438 static CHANSERV_FUNC(cmd_mode
)
4440 struct userData
*uData
;
4441 struct mod_chanmode
*change
;
4446 change
= &channel
->channel_info
->modes
;
4447 if(change
->modes_set
|| change
->modes_clear
) {
4448 modcmd_chanmode_announce(change
);
4449 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4451 reply("CSMSG_NO_MODES", channel
->name
);
4455 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4457 base_oplevel
= MAXOPLEVEL
;
4458 else if (uData
->access
>= UL_OWNER
)
4461 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4462 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4466 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4470 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4471 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4474 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4475 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4479 modcmd_chanmode_announce(change
);
4480 mod_chanmode_free(change
);
4481 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4485 static CHANSERV_FUNC(cmd_invite
)
4487 struct userData
*uData
;
4488 struct userNode
*invite
;
4490 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4494 if(!(invite
= GetUserH(argv
[1])))
4496 reply("MSG_NICK_UNKNOWN", argv
[1]);
4503 if(GetUserMode(channel
, invite
))
4505 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4513 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4514 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4517 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4520 if (invite
->handle_info
->ignores
->used
&& (argc
> 1)) {
4522 for (i
=0; i
< invite
->handle_info
->ignores
->used
; i
++) {
4523 if (user_matches_glob(user
, invite
->handle_info
->ignores
->list
[i
], MATCH_USENICK
)) {
4524 reply("CSMSG_CANNOT_INVITE", argv
[1], channel
->name
);
4530 irc_invite(chanserv
, invite
, channel
);
4532 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4537 static CHANSERV_FUNC(cmd_inviteme
)
4539 if(GetUserMode(channel
, user
))
4541 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4544 if(channel
->channel_info
4545 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4547 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4550 irc_invite(cmd
->parent
->bot
, user
, channel
);
4555 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4558 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4560 /* We display things based on two dimensions:
4561 * - Issue time: present or absent
4562 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4563 * (in order of precedence, so something both expired and revoked
4564 * only counts as revoked)
4566 combo
= (suspended
->issued
? 4 : 0)
4567 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4569 case 0: /* no issue time, indefinite expiration */
4570 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4572 case 1: /* no issue time, expires in future */
4573 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4574 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4576 case 2: /* no issue time, expired */
4577 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4578 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4580 case 3: /* no issue time, revoked */
4581 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4582 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4584 case 4: /* issue time set, indefinite expiration */
4585 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4586 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4588 case 5: /* issue time set, expires in future */
4589 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4590 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4591 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4593 case 6: /* issue time set, expired */
4594 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4595 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4596 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4598 case 7: /* issue time set, revoked */
4599 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4600 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4601 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4604 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4610 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4613 const char *fmt
= "%a %b %d %H:%M %Y";
4614 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4616 if(giveownership
->staff_issuer
)
4618 if(giveownership
->reason
)
4619 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4620 giveownership
->target
, giveownership
->target_access
,
4621 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4623 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4624 giveownership
->target
, giveownership
->target_access
,
4625 giveownership
->staff_issuer
, buf
);
4629 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4634 static CHANSERV_FUNC(cmd_info
)
4636 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4637 struct userData
*uData
, *owner
;
4638 struct chanData
*cData
;
4639 struct do_not_register
*dnr
;
4644 cData
= channel
->channel_info
;
4645 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4646 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4649 uData
= GetChannelUser(cData
, user
->handle_info
);
4650 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4652 mod_chanmode_format(&cData
->modes
, modes
);
4653 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4654 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4657 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4661 note
= iter_data(it
);
4662 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4665 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4666 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4669 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4670 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4671 if(owner
->access
== UL_OWNER
)
4672 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4673 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4674 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4675 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4677 privileged
= IsStaff(user
);
4679 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4680 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4681 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4683 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4684 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4686 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4688 struct suspended
*suspended
;
4689 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4690 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4691 show_suspension_info(cmd
, user
, suspended
);
4693 else if(IsSuspended(cData
))
4695 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4696 show_suspension_info(cmd
, user
, cData
->suspended
);
4698 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4700 struct giveownership
*giveownership
;
4701 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4702 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4703 show_giveownership_info(cmd
, user
, giveownership
);
4705 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4706 reply("CSMSG_CHANNEL_END");
4708 reply("CSMSG_CHANNEL_END_CLEAN");
4712 static CHANSERV_FUNC(cmd_netinfo
)
4714 extern time_t boot_time
;
4715 extern unsigned long burst_length
;
4716 char interval
[INTERVALLEN
];
4718 reply("CSMSG_NETWORK_INFO");
4719 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4720 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4721 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4722 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4723 reply("CSMSG_NETWORK_LAMERS", banCount
);
4724 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4725 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4726 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4731 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4733 struct helpfile_table table
;
4735 struct userNode
*user
;
4740 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4741 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4742 for(nn
=0; nn
<list
->used
; nn
++)
4744 user
= list
->list
[nn
];
4745 if(user
->modes
& skip_flags
)
4749 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4752 nick
= alloca(strlen(user
->nick
)+3);
4753 sprintf(nick
, "(%s)", user
->nick
);
4757 table
.contents
[table
.length
][0] = nick
;
4760 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4763 static CHANSERV_FUNC(cmd_ircops
)
4765 reply("CSMSG_STAFF_OPERS");
4766 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4770 static CHANSERV_FUNC(cmd_helpers
)
4772 reply("CSMSG_STAFF_HELPERS");
4773 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4777 static CHANSERV_FUNC(cmd_staff
)
4779 reply("CSMSG_NETWORK_STAFF");
4780 cmd_ircops(CSFUNC_ARGS
);
4781 cmd_helpers(CSFUNC_ARGS
);
4785 static CHANSERV_FUNC(cmd_peek
)
4787 struct modeNode
*mn
;
4788 char modes
[MODELEN
];
4790 struct helpfile_table table
;
4792 irc_make_chanmode(channel
, modes
);
4794 reply("CSMSG_PEEK_INFO", channel
->name
);
4795 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4797 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4798 reply("CSMSG_PEEK_MODES", modes
);
4799 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4803 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4804 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4805 for(n
= 0; n
< channel
->members
.used
; n
++)
4807 mn
= channel
->members
.list
[n
];
4808 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4810 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4811 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4816 reply("CSMSG_PEEK_OPS");
4817 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4820 reply("CSMSG_PEEK_NO_OPS");
4821 reply("CSMSG_PEEK_END");
4825 static MODCMD_FUNC(cmd_wipeinfo
)
4827 struct handle_info
*victim
;
4828 struct userData
*ud
, *actor
;
4831 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4832 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4834 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4836 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4839 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4841 reply("MSG_USER_OUTRANKED", victim
->handle
);
4847 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4852 resync_channel(struct chanNode
*channel
)
4854 struct mod_chanmode
*changes
;
4855 struct chanData
*cData
= channel
->channel_info
;
4856 unsigned int ii
, used
;
4858 /* 6 = worst case -ovh+ovh on everyone */
4859 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4860 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4862 struct modeNode
*mn
= channel
->members
.list
[ii
];
4863 struct userData
*uData
;
4865 if(IsService(mn
->user
))
4869 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4871 /* If the channel is in no-mode mode, de-mode EVERYONE */
4872 if(cData
->chOpts
[chAutomode
] == 'n')
4876 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4877 changes
->args
[used
++].u
.member
= mn
;
4880 else /* Give various userlevels their modes.. */
4882 if(uData
&& uData
->access
>= UL_OP
)
4884 if(!(mn
->modes
& MODE_CHANOP
))
4886 changes
->args
[used
].mode
= MODE_CHANOP
;
4887 changes
->args
[used
++].u
.member
= mn
;
4890 else if(uData
&& uData
->access
>= UL_HALFOP
)
4892 if(mn
->modes
& MODE_CHANOP
)
4894 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4895 changes
->args
[used
++].u
.member
= mn
;
4897 if(!(mn
->modes
& MODE_HALFOP
))
4899 changes
->args
[used
].mode
= MODE_HALFOP
;
4900 changes
->args
[used
++].u
.member
= mn
;
4903 else if(uData
&& uData
->access
>= UL_PEON
)
4905 if(mn
->modes
& MODE_CHANOP
)
4907 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4908 changes
->args
[used
++].u
.member
= mn
;
4910 if(mn
->modes
& MODE_HALFOP
)
4912 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4913 changes
->args
[used
++].u
.member
= mn
;
4915 /* Don't voice peons if were in mode m */
4916 if( cData
->chOpts
[chAutomode
] == 'm')
4918 if(mn
->modes
& MODE_VOICE
)
4920 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4921 changes
->args
[used
++].u
.member
= mn
;
4924 /* otherwise, make user they do have voice */
4925 else if(!(mn
->modes
& MODE_VOICE
))
4927 changes
->args
[used
].mode
= MODE_VOICE
;
4928 changes
->args
[used
++].u
.member
= mn
;
4931 else /* They arnt on the userlist.. */
4933 /* If we voice everyone, but they dont.. */
4934 if(cData
->chOpts
[chAutomode
] == 'v')
4936 /* Remove anything except v */
4937 if(mn
->modes
& ~MODE_VOICE
)
4939 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4940 changes
->args
[used
++].u
.member
= mn
;
4943 if(!(mn
->modes
& MODE_VOICE
))
4945 changes
->args
[used
].mode
= MODE_VOICE
;
4946 changes
->args
[used
++].u
.member
= mn
;
4949 /* If we hop everyone, but they dont.. */
4950 else if(cData
->chOpts
[chAutomode
] == 'h')
4952 /* Remove anything except h */
4953 if(mn
->modes
& ~MODE_HALFOP
)
4955 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4956 changes
->args
[used
++].u
.member
= mn
;
4959 if(!(mn
->modes
& MODE_HALFOP
))
4961 changes
->args
[used
].mode
= MODE_HALFOP
;
4962 changes
->args
[used
++].u
.member
= mn
;
4965 /* If we op everyone, but they dont.. */
4966 else if(cData
->chOpts
[chAutomode
] == 'o')
4968 /* Remove anything except h */
4969 if(mn
->modes
& ~MODE_CHANOP
)
4971 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4972 changes
->args
[used
++].u
.member
= mn
;
4975 if(!(mn
->modes
& MODE_CHANOP
))
4977 changes
->args
[used
].mode
= MODE_CHANOP
;
4978 changes
->args
[used
++].u
.member
= mn
;
4981 /* they have no excuse for having modes, de-everything them */
4986 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4987 changes
->args
[used
++].u
.member
= mn
;
4993 changes
->argc
= used
;
4994 mod_chanmode_announce(chanserv
, channel
, changes
);
4995 mod_chanmode_free(changes
);
4998 static CHANSERV_FUNC(cmd_resync
)
5000 resync_channel(channel
);
5001 reply("CSMSG_RESYNCED_USERS", channel
->name
);
5005 static CHANSERV_FUNC(cmd_seen
)
5007 struct userData
*uData
;
5008 struct handle_info
*handle
;
5009 char seen
[INTERVALLEN
];
5013 if(!irccasecmp(argv
[1], chanserv
->nick
))
5015 reply("CSMSG_IS_CHANSERV");
5019 if(!(handle
= get_handle_info(argv
[1])))
5021 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
5025 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
5027 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
5032 reply("CSMSG_USER_PRESENT", handle
->handle
);
5033 else if(uData
->seen
)
5034 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
5036 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
5038 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
5039 reply("CSMSG_USER_VACATION", handle
->handle
);
5044 static MODCMD_FUNC(cmd_names
)
5046 struct userNode
*targ
;
5047 struct userData
*targData
;
5048 unsigned int ii
, pos
;
5051 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
5053 targ
= channel
->members
.list
[ii
]->user
;
5054 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
5057 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
5060 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5064 if(IsUserSuspended(targData
))
5066 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5069 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5070 reply("CSMSG_END_NAMES", channel
->name
);
5075 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5077 switch(ntype
->visible_type
)
5079 case NOTE_VIS_ALL
: return 1;
5080 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5081 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5086 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5088 struct userData
*uData
;
5090 switch(ntype
->set_access_type
)
5092 case NOTE_SET_CHANNEL_ACCESS
:
5093 if(!user
->handle_info
)
5095 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5097 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5098 case NOTE_SET_CHANNEL_SETTER
:
5099 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5100 case NOTE_SET_PRIVILEGED
: default:
5101 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5105 static CHANSERV_FUNC(cmd_note
)
5107 struct chanData
*cData
;
5109 struct note_type
*ntype
;
5111 cData
= channel
->channel_info
;
5114 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5118 /* If no arguments, show all visible notes for the channel. */
5124 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5126 note
= iter_data(it
);
5127 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5130 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5131 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5134 reply("CSMSG_NOTELIST_END", channel
->name
);
5136 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5138 /* If one argument, show the named note. */
5141 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5142 && note_type_visible_to_user(cData
, note
->type
, user
))
5144 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5146 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5147 && note_type_visible_to_user(NULL
, ntype
, user
))
5149 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5154 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5158 /* Assume they're trying to set a note. */
5162 ntype
= dict_find(note_types
, argv
[1], NULL
);
5165 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5168 else if(note_type_settable_by_user(channel
, ntype
, user
))
5170 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5171 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5172 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5173 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5174 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5176 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5178 /* The note is viewable to staff only, so return 0
5179 to keep the invocation from getting logged (or
5180 regular users can see it in !events). */
5186 reply("CSMSG_NO_ACCESS");
5193 static CHANSERV_FUNC(cmd_delnote
)
5198 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5199 || !note_type_settable_by_user(channel
, note
->type
, user
))
5201 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5204 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5205 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5209 static CHANSERV_FUNC(cmd_last
)
5215 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5217 if(numoflines
< 1 || numoflines
> 200)
5219 reply("CSMSG_LAST_INVALID");
5222 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5226 static CHANSERV_FUNC(cmd_events
)
5228 struct logSearch discrim
;
5229 struct logReport report
;
5230 unsigned int matches
, limit
;
5232 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5233 if(limit
< 1 || limit
> 200)
5236 memset(&discrim
, 0, sizeof(discrim
));
5237 discrim
.masks
.bot
= chanserv
;
5238 discrim
.masks
.channel_name
= channel
->name
;
5240 discrim
.masks
.command
= argv
[2];
5241 discrim
.limit
= limit
;
5242 discrim
.max_time
= INT_MAX
;
5243 discrim
.severities
= 1 << LOG_COMMAND
;
5244 report
.reporter
= chanserv
;
5246 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5247 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5249 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5251 reply("MSG_MATCH_COUNT", matches
);
5253 reply("MSG_NO_MATCHES");
5257 static CHANSERV_FUNC(cmd_say
)
5263 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5264 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5266 else if(GetUserH(argv
[1]))
5269 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5270 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5274 reply("MSG_NOT_TARGET_NAME");
5280 static CHANSERV_FUNC(cmd_emote
)
5286 /* CTCP is so annoying. */
5287 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5288 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5290 else if(GetUserH(argv
[1]))
5292 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5293 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5297 reply("MSG_NOT_TARGET_NAME");
5303 struct channelList
*
5304 chanserv_support_channels(void)
5306 return &chanserv_conf
.support_channels
;
5309 static CHANSERV_FUNC(cmd_expire
)
5311 int channel_count
= registered_channels
;
5312 expire_channels(NULL
);
5313 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5318 chanserv_expire_suspension(void *data
)
5320 struct suspended
*suspended
= data
;
5321 struct chanNode
*channel
;
5323 if(!suspended
->expires
|| (now
< suspended
->expires
))
5324 suspended
->revoked
= now
;
5325 channel
= suspended
->cData
->channel
;
5326 suspended
->cData
->channel
= channel
;
5327 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5328 if(!IsOffChannel(suspended
->cData
))
5330 spamserv_cs_suspend(channel
, 0, 0, NULL
);
5331 ss_cs_join_channel(channel
, 1);
5335 static CHANSERV_FUNC(cmd_csuspend
)
5337 struct suspended
*suspended
;
5338 char reason
[MAXLEN
];
5339 time_t expiry
, duration
;
5340 struct userData
*uData
;
5344 if(IsProtected(channel
->channel_info
))
5346 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5350 if(argv
[1][0] == '!')
5352 else if(IsSuspended(channel
->channel_info
))
5354 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5355 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5359 if(!strcmp(argv
[1], "0"))
5361 else if((duration
= ParseInterval(argv
[1])))
5362 expiry
= now
+ duration
;
5365 reply("MSG_INVALID_DURATION", argv
[1]);
5369 unsplit_string(argv
+ 2, argc
- 2, reason
);
5371 suspended
= calloc(1, sizeof(*suspended
));
5372 suspended
->revoked
= 0;
5373 suspended
->issued
= now
;
5374 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5375 suspended
->expires
= expiry
;
5376 suspended
->reason
= strdup(reason
);
5377 suspended
->cData
= channel
->channel_info
;
5378 suspended
->previous
= suspended
->cData
->suspended
;
5379 suspended
->cData
->suspended
= suspended
;
5381 if(suspended
->expires
)
5382 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5384 if(IsSuspended(channel
->channel_info
))
5386 suspended
->previous
->revoked
= now
;
5387 if(suspended
->previous
->expires
)
5388 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5389 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5390 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5394 /* Mark all users in channel as absent. */
5395 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5404 /* Mark the channel as suspended, then part. */
5405 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5406 spamserv_cs_suspend(channel
, expiry
, 1, suspended
->reason
);
5407 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5408 reply("CSMSG_SUSPENDED", channel
->name
);
5409 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5410 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5415 static CHANSERV_FUNC(cmd_cunsuspend
)
5417 struct suspended
*suspended
;
5418 char message
[MAXLEN
];
5420 if(!IsSuspended(channel
->channel_info
))
5422 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5426 suspended
= channel
->channel_info
->suspended
;
5428 /* Expire the suspension and join ChanServ to the channel. */
5429 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5430 chanserv_expire_suspension(suspended
);
5431 reply("CSMSG_UNSUSPENDED", channel
->name
);
5432 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5433 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5437 typedef struct chanservSearch
5445 unsigned long flags
;
5449 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5452 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5457 search
= malloc(sizeof(struct chanservSearch
));
5458 memset(search
, 0, sizeof(*search
));
5461 for(i
= 0; i
< argc
; i
++)
5463 /* Assume all criteria require arguments. */
5466 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5470 if(!irccasecmp(argv
[i
], "name"))
5471 search
->name
= argv
[++i
];
5472 else if(!irccasecmp(argv
[i
], "registrar"))
5473 search
->registrar
= argv
[++i
];
5474 else if(!irccasecmp(argv
[i
], "unvisited"))
5475 search
->unvisited
= ParseInterval(argv
[++i
]);
5476 else if(!irccasecmp(argv
[i
], "registered"))
5477 search
->registered
= ParseInterval(argv
[++i
]);
5478 else if(!irccasecmp(argv
[i
], "flags"))
5481 if(!irccasecmp(argv
[i
], "nodelete"))
5482 search
->flags
|= CHANNEL_NODELETE
;
5483 else if(!irccasecmp(argv
[i
], "suspended"))
5484 search
->flags
|= CHANNEL_SUSPENDED
;
5487 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5491 else if(!irccasecmp(argv
[i
], "limit"))
5492 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5495 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5500 if(search
->name
&& !strcmp(search
->name
, "*"))
5502 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5503 search
->registrar
= 0;
5512 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5514 const char *name
= channel
->channel
->name
;
5515 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5516 (search
->registrar
&& !channel
->registrar
) ||
5517 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5518 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5519 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5520 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5527 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5529 struct chanData
*channel
;
5530 unsigned int matches
= 0;
5532 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5534 if(!chanserv_channel_match(channel
, search
))
5544 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5549 search_print(struct chanData
*channel
, void *data
)
5551 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5554 static CHANSERV_FUNC(cmd_search
)
5557 unsigned int matches
;
5558 channel_search_func action
;
5562 if(!irccasecmp(argv
[1], "count"))
5563 action
= search_count
;
5564 else if(!irccasecmp(argv
[1], "print"))
5565 action
= search_print
;
5568 reply("CSMSG_ACTION_INVALID", argv
[1]);
5572 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5576 if(action
== search_count
)
5577 search
->limit
= INT_MAX
;
5579 if(action
== search_print
)
5581 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5582 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5586 matches
= chanserv_channel_search(search
, action
, user
);
5589 reply("MSG_MATCH_COUNT", matches
);
5591 reply("MSG_NO_MATCHES");
5597 static CHANSERV_FUNC(cmd_unvisited
)
5599 struct chanData
*cData
;
5600 time_t interval
= chanserv_conf
.channel_expire_delay
;
5601 char buffer
[INTERVALLEN
];
5602 unsigned int limit
= 25, matches
= 0;
5606 interval
= ParseInterval(argv
[1]);
5608 limit
= atoi(argv
[2]);
5611 intervalString(buffer
, interval
, user
->handle_info
);
5612 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5614 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5616 if((now
- cData
->visited
) < interval
)
5619 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5620 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5627 static MODCMD_FUNC(chan_opt_defaulttopic
)
5633 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5635 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5639 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5641 free(channel
->channel_info
->topic
);
5642 if(topic
[0] == '*' && topic
[1] == 0)
5644 topic
= channel
->channel_info
->topic
= NULL
;
5648 topic
= channel
->channel_info
->topic
= strdup(topic
);
5649 if(channel
->channel_info
->topic_mask
5650 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5651 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5653 SetChannelTopic(channel
, chanserv
, chanserv
, topic
? topic
: "", 1);
5656 if(channel
->channel_info
->topic
)
5657 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5659 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5663 static MODCMD_FUNC(chan_opt_topicmask
)
5667 struct chanData
*cData
= channel
->channel_info
;
5670 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5672 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5676 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5678 if(cData
->topic_mask
)
5679 free(cData
->topic_mask
);
5680 if(mask
[0] == '*' && mask
[1] == 0)
5682 cData
->topic_mask
= 0;
5686 cData
->topic_mask
= strdup(mask
);
5688 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5689 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5690 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5694 if(channel
->channel_info
->topic_mask
)
5695 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5697 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5701 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5705 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5709 if(greeting
[0] == '*' && greeting
[1] == 0)
5713 unsigned int length
= strlen(greeting
);
5714 if(length
> chanserv_conf
.greeting_length
)
5716 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5719 *data
= strdup(greeting
);
5728 reply(name
, user_find_message(user
, "MSG_NONE"));
5732 static MODCMD_FUNC(chan_opt_greeting
)
5734 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5737 static MODCMD_FUNC(chan_opt_usergreeting
)
5739 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5742 static MODCMD_FUNC(chan_opt_modes
)
5744 struct mod_chanmode
*new_modes
;
5745 char modes
[MODELEN
];
5749 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5751 reply("CSMSG_NO_ACCESS");
5754 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5756 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5758 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5760 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5763 else if(new_modes
->argc
> 1)
5765 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5766 mod_chanmode_free(new_modes
);
5771 channel
->channel_info
->modes
= *new_modes
;
5772 modcmd_chanmode_announce(new_modes
);
5773 mod_chanmode_free(new_modes
);
5777 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5779 reply("CSMSG_SET_MODES", modes
);
5781 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5785 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5787 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5789 struct chanData
*cData
= channel
->channel_info
;
5794 /* Set flag according to value. */
5795 if(enabled_string(argv
[1]))
5797 cData
->flags
|= mask
;
5800 else if(disabled_string(argv
[1]))
5802 cData
->flags
&= ~mask
;
5807 reply("MSG_INVALID_BINARY", argv
[1]);
5813 /* Find current option value. */
5814 value
= (cData
->flags
& mask
) ? 1 : 0;
5818 reply(name
, user_find_message(user
, "MSG_ON"));
5820 reply(name
, user_find_message(user
, "MSG_OFF"));
5824 static MODCMD_FUNC(chan_opt_nodelete
)
5826 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5828 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5832 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5835 static MODCMD_FUNC(chan_opt_dynlimit
)
5837 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5840 static MODCMD_FUNC(chan_opt_offchannel
)
5842 struct chanData
*cData
= channel
->channel_info
;
5847 /* Set flag according to value. */
5848 if(enabled_string(argv
[1]))
5850 if(!IsOffChannel(cData
))
5851 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5852 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5855 else if(disabled_string(argv
[1]))
5857 if(IsOffChannel(cData
))
5859 struct mod_chanmode change
;
5860 mod_chanmode_init(&change
);
5862 change
.args
[0].mode
= MODE_CHANOP
;
5863 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5864 mod_chanmode_announce(chanserv
, channel
, &change
);
5866 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5871 reply("MSG_INVALID_BINARY", argv
[1]);
5877 /* Find current option value. */
5878 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5882 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5884 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5888 static MODCMD_FUNC(chan_opt_defaults
)
5890 struct userData
*uData
;
5891 struct chanData
*cData
;
5892 const char *confirm
;
5893 enum levelOption lvlOpt
;
5894 enum charOption chOpt
;
5896 cData
= channel
->channel_info
;
5897 uData
= GetChannelUser(cData
, user
->handle_info
);
5898 if(!uData
|| (uData
->access
< UL_OWNER
))
5900 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5903 confirm
= make_confirmation_string(uData
);
5904 if((argc
< 2) || strcmp(argv
[1], confirm
))
5906 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5909 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5910 cData
->modes
= chanserv_conf
.default_modes
;
5911 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5912 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5913 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5914 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5915 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5920 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5922 struct chanData
*cData
= channel
->channel_info
;
5923 struct userData
*uData
;
5924 unsigned short value
;
5928 if(!check_user_level(channel
, user
, option
, 1, 1))
5930 reply("CSMSG_CANNOT_SET");
5933 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5934 if(!value
&& strcmp(argv
[1], "0"))
5936 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5939 uData
= GetChannelUser(cData
, user
->handle_info
);
5940 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5942 reply("CSMSG_BAD_SETLEVEL");
5948 /* This test only applies to owners, since non-owners
5949 * trying to set an option to above their level get caught
5950 * by the CSMSG_BAD_SETLEVEL test above.
5952 if(value
> uData
->access
)
5954 reply("CSMSG_BAD_SETTERS");
5961 cData
->lvlOpts
[option
] = value
;
5963 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5967 static MODCMD_FUNC(chan_opt_enfops
)
5969 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5972 static MODCMD_FUNC(chan_opt_enfhalfops
)
5974 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5976 static MODCMD_FUNC(chan_opt_enfmodes
)
5978 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5981 static MODCMD_FUNC(chan_opt_enftopic
)
5983 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5986 static MODCMD_FUNC(chan_opt_pubcmd
)
5988 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5991 static MODCMD_FUNC(chan_opt_setters
)
5993 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5996 static MODCMD_FUNC(chan_opt_userinfo
)
5998 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
6001 static MODCMD_FUNC(chan_opt_topicsnarf
)
6003 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
6006 static MODCMD_FUNC(chan_opt_inviteme
)
6008 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
6011 /* TODO: Make look like this when no args are
6013 * -X3- -------------------------------
6014 * -X3- BanTimeout: Bans are removed:
6015 * -X3- ----- * indicates current -----
6016 * -X3- 0: [*] Never.
6017 * -X3- 1: [ ] After 10 minutes.
6018 * -X3- 2: [ ] After 2 hours.
6019 * -X3- 3: [ ] After 4 hours.
6020 * -X3- 4: [ ] After 24 hours.
6021 * -X3- 5: [ ] After one week.
6022 * -X3- ------------- End -------------
6025 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6027 struct chanData
*cData
= channel
->channel_info
;
6028 int count
= charOptions
[option
].count
, index
;
6032 index
= atoi(argv
[1]);
6034 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
6036 reply("CSMSG_INVALID_NUMERIC", index
);
6037 /* Show possible values. */
6038 for(index
= 0; index
< count
; index
++)
6039 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6043 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
6047 /* Find current option value. */
6050 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
6054 /* Somehow, the option value is corrupt; reset it to the default. */
6055 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
6060 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
6064 static MODCMD_FUNC(chan_opt_automode
)
6066 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6069 static MODCMD_FUNC(chan_opt_protect
)
6071 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6074 static MODCMD_FUNC(chan_opt_toys
)
6076 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6079 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6081 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6084 static MODCMD_FUNC(chan_opt_bantimeout
)
6086 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6089 static MODCMD_FUNC(chan_opt_topicrefresh
)
6091 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6094 static MODCMD_FUNC(chan_opt_resync
)
6096 return channel_multiple_option(chResync
, CSFUNC_ARGS
);
6099 static struct svccmd_list set_shows_list
;
6102 handle_svccmd_unbind(struct svccmd
*target
) {
6104 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6105 if(target
== set_shows_list
.list
[ii
])
6106 set_shows_list
.used
= 0;
6109 static CHANSERV_FUNC(cmd_set
)
6111 struct svccmd
*subcmd
;
6115 /* Check if we need to (re-)initialize set_shows_list. */
6116 if(!set_shows_list
.used
)
6118 if(!set_shows_list
.size
)
6120 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6121 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6123 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6125 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6126 sprintf(buf
, "%s %s", argv
[0], name
);
6127 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6130 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6133 svccmd_list_append(&set_shows_list
, subcmd
);
6139 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6140 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6142 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6144 subcmd
= set_shows_list
.list
[ii
];
6145 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6147 reply("CSMSG_CHANNEL_OPTIONS_END");
6151 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6152 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6155 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6158 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6160 reply("CSMSG_NO_ACCESS");
6164 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6168 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6170 struct userData
*uData
;
6172 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6175 reply("CSMSG_NOT_USER", channel
->name
);
6181 /* Just show current option value. */
6183 else if(enabled_string(argv
[1]))
6185 uData
->flags
|= mask
;
6187 else if(disabled_string(argv
[1]))
6189 uData
->flags
&= ~mask
;
6193 reply("MSG_INVALID_BINARY", argv
[1]);
6197 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6201 static MODCMD_FUNC(user_opt_autoop
)
6203 struct userData
*uData
;
6205 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6208 reply("CSMSG_NOT_USER", channel
->name
);
6211 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6212 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6214 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6215 /* TODO: add halfops error message? or is the op one generic enough? */
6218 static MODCMD_FUNC(user_opt_autoinvite
)
6220 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6223 static MODCMD_FUNC(user_opt_info
)
6225 struct userData
*uData
;
6228 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6232 /* If they got past the command restrictions (which require access)
6233 * but fail this test, we have some fool with security override on.
6235 reply("CSMSG_NOT_USER", channel
->name
);
6242 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6243 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6245 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6248 bp
= strcspn(infoline
, "\001");
6251 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6256 if(infoline
[0] == '*' && infoline
[1] == 0)
6259 uData
->info
= strdup(infoline
);
6262 reply("CSMSG_USET_INFO", uData
->info
);
6264 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6268 struct svccmd_list uset_shows_list
;
6270 static CHANSERV_FUNC(cmd_uset
)
6272 struct svccmd
*subcmd
;
6276 /* Check if we need to (re-)initialize uset_shows_list. */
6277 if(!uset_shows_list
.used
)
6281 "AutoOp", "AutoInvite", "Info"
6284 if(!uset_shows_list
.size
)
6286 uset_shows_list
.size
= ArrayLength(options
);
6287 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6289 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6291 const char *name
= options
[ii
];
6292 sprintf(buf
, "%s %s", argv
[0], name
);
6293 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6296 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6299 svccmd_list_append(&uset_shows_list
, subcmd
);
6305 /* Do this so options are presented in a consistent order. */
6306 reply("CSMSG_USER_OPTIONS");
6307 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6308 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6312 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6313 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6316 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6320 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6323 static CHANSERV_FUNC(cmd_giveownership
)
6325 struct handle_info
*new_owner_hi
;
6326 struct userData
*new_owner
, *curr_user
;
6327 struct chanData
*cData
= channel
->channel_info
;
6328 struct do_not_register
*dnr
;
6329 struct giveownership
*giveownership
;
6330 unsigned int force
, override
;
6331 unsigned short co_access
, new_owner_old_access
;
6332 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6335 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6336 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6338 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6339 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6340 && (uData
->access
> 500)
6341 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6342 || uData
->access
< 500));
6345 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6347 struct userData
*owner
= NULL
;
6348 for(curr_user
= channel
->channel_info
->users
;
6350 curr_user
= curr_user
->next
)
6352 if(curr_user
->access
!= UL_OWNER
)
6356 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6363 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6365 char delay
[INTERVALLEN
];
6366 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6367 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6370 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6372 if(new_owner_hi
== user
->handle_info
)
6374 reply("CSMSG_NO_TRANSFER_SELF");
6377 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6382 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6386 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6390 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6392 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6395 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6396 if(!IsHelping(user
))
6397 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6399 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6403 new_owner_old_access
= new_owner
->access
;
6404 if(new_owner
->access
>= UL_COOWNER
)
6405 co_access
= new_owner
->access
;
6407 co_access
= UL_COOWNER
;
6408 new_owner
->access
= UL_OWNER
;
6410 curr_user
->access
= co_access
;
6411 cData
->ownerTransfer
= now
;
6413 giveownership
= calloc(1, sizeof(*giveownership
));
6414 giveownership
->issued
= now
;
6415 giveownership
->old_owner
= curr_user
->handle
->handle
;
6416 giveownership
->target
= new_owner_hi
->handle
;
6417 giveownership
->target_access
= new_owner_old_access
;
6420 if(argc
> (2 + force
))
6422 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6423 giveownership
->reason
= strdup(transfer_reason
);
6425 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6428 giveownership
->previous
= channel
->channel_info
->giveownership
;
6429 channel
->channel_info
->giveownership
= giveownership
;
6431 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6432 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6433 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6438 chanserv_expire_user_suspension(void *data
)
6440 struct userData
*target
= data
;
6442 target
->expires
= 0;
6443 target
->flags
&= ~USER_SUSPENDED
;
6446 static CHANSERV_FUNC(cmd_suspend
)
6448 struct handle_info
*hi
;
6449 struct userData
*self
, *target
;
6453 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6454 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6455 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6457 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6460 if(target
->access
>= self
->access
)
6462 reply("MSG_USER_OUTRANKED", hi
->handle
);
6465 if(target
->flags
& USER_SUSPENDED
)
6467 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6472 target
->present
= 0;
6475 if(!strcmp(argv
[2], "0"))
6479 unsigned int duration
;
6480 if(!(duration
= ParseInterval(argv
[2])))
6482 reply("MSG_INVALID_DURATION", argv
[2]);
6485 expiry
= now
+ duration
;
6488 target
->expires
= expiry
;
6491 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6493 target
->flags
|= USER_SUSPENDED
;
6494 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6498 static CHANSERV_FUNC(cmd_unsuspend
)
6500 struct handle_info
*hi
;
6501 struct userData
*self
, *target
;
6504 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6505 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6506 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6508 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6511 if(target
->access
>= self
->access
)
6513 reply("MSG_USER_OUTRANKED", hi
->handle
);
6516 if(!(target
->flags
& USER_SUSPENDED
))
6518 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6521 target
->flags
&= ~USER_SUSPENDED
;
6522 scan_user_presence(target
, NULL
);
6523 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6524 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6528 static MODCMD_FUNC(cmd_deleteme
)
6530 struct handle_info
*hi
;
6531 struct userData
*target
;
6532 const char *confirm_string
;
6533 unsigned short access
;
6536 hi
= user
->handle_info
;
6537 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6539 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6542 if(target
->access
== UL_OWNER
)
6544 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6547 confirm_string
= make_confirmation_string(target
);
6548 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6550 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6553 access
= target
->access
;
6554 channel_name
= strdup(channel
->name
);
6555 del_channel_user(target
, 1);
6556 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6562 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6564 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6565 struct chanData
*cData
;
6568 for(cData
= channelList
; cData
; cData
= cData
->next
)
6570 if(IsSuspended(cData
))
6572 opt
= cData
->chOpts
[chTopicRefresh
];
6575 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6578 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6579 cData
->last_refresh
= refresh_num
;
6581 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6585 chanserv_auto_resync(UNUSED_ARG(void *data
))
6587 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6588 struct chanData
*cData
;
6591 for(cData
= channelList
; cData
; cData
= cData
->next
)
6593 if(IsSuspended(cData
)) continue;
6594 opt
= cData
->chOpts
[chResync
];
6595 if(opt
== 'n') continue;
6596 if((refresh_num
- cData
->last_resync
) < (unsigned int)(1 << (opt
- '1'))) continue;
6597 resync_channel(cData
->channel
);
6598 cData
->last_resync
= refresh_num
;
6600 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_auto_resync
, NULL
);
6603 static CHANSERV_FUNC(cmd_unf
)
6607 char response
[MAXLEN
];
6608 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6609 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6610 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6613 reply("CSMSG_UNF_RESPONSE");
6617 static CHANSERV_FUNC(cmd_ping
)
6621 char response
[MAXLEN
];
6622 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6623 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6624 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6627 reply("CSMSG_PING_RESPONSE");
6631 static CHANSERV_FUNC(cmd_wut
)
6635 char response
[MAXLEN
];
6636 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6637 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6638 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6641 reply("CSMSG_WUT_RESPONSE");
6646 static CHANSERV_FUNC(cmd_8ball
)
6648 unsigned int i
, j
, accum
;
6653 for(i
=1; i
<argc
; i
++)
6654 for(j
=0; argv
[i
][j
]; j
++)
6655 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6656 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6659 char response
[MAXLEN
];
6660 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6661 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6664 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6668 #else /* Use cool 8ball instead */
6670 void eightball(char *outcome
, int method
, unsigned int seed
)
6674 #define NUMOFCOLORS 18
6675 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6676 "white", "black", "grey", "brown",
6677 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6678 "fuchsia","turquoise","magenta", "cyan"};
6679 #define NUMOFLOCATIONS 50
6680 char balllocations
[50][55] = {
6681 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6682 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6683 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6684 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6685 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6686 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6687 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6688 "your bra", "your hair", "your bed", "the couch", "the wall",
6689 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6690 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6691 #define NUMOFPREPS 15
6692 char ballpreps
[50][50] = {
6693 "Near", "Somewhere near", "In", "In", "In",
6694 "In", "Hiding in", "Under", "Next to", "Over",
6695 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6696 #define NUMOFNUMS 34
6697 char ballnums
[50][50] = {
6698 "A hundred", "A thousand", "A few", "42",
6699 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6700 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6701 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6703 #define NUMOFMULTS 8
6704 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6707 * 0: normal (Not used in x3)
6714 if (method
== 1) /* A Color */
6718 answer
= (rand() % 12); /* Make sure this is the # of entries */
6721 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6723 case 1: strcpy(tmp
, "Sort of a light %s color.");
6725 case 2: strcpy(tmp
, "Dark and dreary %s.");
6727 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6729 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6731 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6733 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6735 case 10: strcpy(tmp
, "Solid %s.");
6737 case 11: strcpy(tmp
, "Transparent %s.");
6739 default: strcpy(outcome
, "An invalid random number was generated.");
6742 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6745 else if (method
== 2) /* Location */
6747 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6749 else if (method
== 3) /* Number of ___ */
6751 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6755 //Debug(DBGWARNING, "Error in 8ball.");
6760 static CHANSERV_FUNC(cmd_8ball
)
6762 char *word1
, *word2
, *word3
;
6763 static char eb
[MAXLEN
];
6764 unsigned int accum
, i
, j
;
6768 for(i
=1; i
<argc
; i
++)
6769 for(j
=0; argv
[i
][j
]; j
++)
6770 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6772 accum
+= time(NULL
)/3600;
6774 word2
= argc
>2?argv
[2]:"";
6775 word3
= argc
>3?argv
[3]:"";
6778 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6779 eightball(eb
, 1, accum
);
6780 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6781 eightball(eb
, 1, accum
);
6782 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6783 eightball(eb
, 1, accum
);
6784 /*** LOCATION *****/
6789 (strcasecmp(word1
, "where") == 0) &&
6790 (strcasecmp(word2
, "is") == 0)
6794 strcasecmp(word1
, "where's") == 0
6797 eightball(eb
, 2, accum
);
6799 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6800 eightball(eb
, 3, accum
);
6804 /* Generic 8ball question.. so pull from x3.conf srvx style */
6807 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6810 char response
[MAXLEN
];
6811 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6812 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6815 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6821 char response
[MAXLEN
];
6822 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6823 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6826 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6831 static CHANSERV_FUNC(cmd_d
)
6833 unsigned long sides
, count
, modifier
, ii
, total
;
6834 char response
[MAXLEN
], *sep
;
6838 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6848 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6849 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6853 else if((sep
[0] == '-') && isdigit(sep
[1]))
6854 modifier
= strtoul(sep
, NULL
, 10);
6855 else if((sep
[0] == '+') && isdigit(sep
[1]))
6856 modifier
= strtoul(sep
+1, NULL
, 10);
6863 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6868 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6871 for(total
= ii
= 0; ii
< count
; ++ii
)
6872 total
+= (rand() % sides
) + 1;
6875 if((count
> 1) || modifier
)
6877 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6878 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6882 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6883 sprintf(response
, fmt
, total
, sides
);
6886 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6888 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6892 static CHANSERV_FUNC(cmd_huggle
)
6894 /* CTCP must be via PRIVMSG, never notice */
6896 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6898 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6902 static CHANSERV_FUNC(cmd_calc
)
6904 char response
[MAXLEN
];
6907 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6910 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6912 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6917 chanserv_adjust_limit(void *data
)
6919 struct mod_chanmode change
;
6920 struct chanData
*cData
= data
;
6921 struct chanNode
*channel
= cData
->channel
;
6924 if(IsSuspended(cData
))
6927 cData
->limitAdjusted
= now
;
6928 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6929 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6931 if(limit
> cData
->modes
.new_limit
)
6932 limit
= cData
->modes
.new_limit
;
6933 else if(limit
== cData
->modes
.new_limit
)
6937 mod_chanmode_init(&change
);
6938 change
.modes_set
= MODE_LIMIT
;
6939 change
.new_limit
= limit
;
6940 mod_chanmode_announce(chanserv
, channel
, &change
);
6944 handle_new_channel(struct chanNode
*channel
)
6946 struct chanData
*cData
;
6948 if(!(cData
= channel
->channel_info
))
6951 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6952 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6954 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6955 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6958 /* Welcome to my worst nightmare. Warning: Read (or modify)
6959 the code below at your own risk. */
6961 handle_join(struct modeNode
*mNode
)
6963 struct mod_chanmode change
;
6964 struct userNode
*user
= mNode
->user
;
6965 struct chanNode
*channel
= mNode
->channel
;
6966 struct chanData
*cData
;
6967 struct userData
*uData
= NULL
;
6968 struct banData
*bData
;
6969 struct handle_info
*handle
;
6970 unsigned int modes
= 0, info
= 0;
6973 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6976 cData
= channel
->channel_info
;
6977 if(channel
->members
.used
> cData
->max
)
6978 cData
->max
= channel
->members
.used
;
6981 /* Check for bans. If they're joining through a ban, one of two
6983 * 1: Join during a netburst, by riding the break. Kick them
6984 * unless they have ops or voice in the channel.
6985 * 2: They're allowed to join through the ban (an invite in
6986 * ircu2.10, or a +e on Hybrid, or something).
6987 * If they're not joining through a ban, and the banlist is not
6988 * full, see if they're on the banlist for the channel. If so,
6991 if(user
->uplink
->burst
&& !mNode
->modes
)
6994 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
6996 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
6998 /* Riding a netburst. Naughty. */
6999 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
7006 mod_chanmode_init(&change
);
7008 if(channel
->banlist
.used
< MAXBANS
)
7010 /* Not joining through a ban. */
7011 for(bData
= cData
->bans
;
7012 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
7013 bData
= bData
->next
);
7017 char kick_reason
[MAXLEN
];
7018 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7020 bData
->triggered
= now
;
7021 if(bData
!= cData
->bans
)
7023 /* Shuffle the ban to the head of the list. */
7025 bData
->next
->prev
= bData
->prev
;
7027 bData
->prev
->next
= bData
->next
;
7030 bData
->next
= cData
->bans
;
7033 cData
->bans
->prev
= bData
;
7034 cData
->bans
= bData
;
7037 change
.args
[0].mode
= MODE_BAN
;
7038 change
.args
[0].u
.hostmask
= bData
->mask
;
7039 mod_chanmode_announce(chanserv
, channel
, &change
);
7040 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7045 /* ChanServ will not modify the limits in join-flooded channels.
7046 It will also skip DynLimit processing when the user (or srvx)
7047 is bursting in, because there are likely more incoming. */
7048 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7049 && !user
->uplink
->burst
7050 && !channel
->join_flooded
7051 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
7053 /* The user count has begun "bumping" into the channel limit,
7054 so set a timer to raise the limit a bit. Any previous
7055 timers are removed so three incoming users within the delay
7056 results in one limit change, not three. */
7058 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7059 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7062 /* Give automodes exept during join-floods */
7063 if(!channel
->join_flooded
)
7065 if(cData
->chOpts
[chAutomode
] == 'v')
7066 modes
|= MODE_VOICE
;
7067 else if(cData
->chOpts
[chAutomode
] == 'h')
7068 modes
|= MODE_HALFOP
;
7069 else if(cData
->chOpts
[chAutomode
] == 'o')
7070 modes
|= MODE_CHANOP
;
7073 greeting
= cData
->greeting
;
7074 if(user
->handle_info
)
7076 handle
= user
->handle_info
;
7078 if(IsHelper(user
) && !IsHelping(user
))
7081 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7083 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
7085 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7091 uData
= GetTrueChannelAccess(cData
, handle
);
7092 if(uData
&& !IsUserSuspended(uData
))
7094 /* non users getting automodes are handled above. */
7095 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7097 if(uData
->access
>= UL_OP
)
7098 modes
|= MODE_CHANOP
;
7099 else if(uData
->access
>= UL_HALFOP
)
7100 modes
|= MODE_HALFOP
;
7101 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7102 modes
|= MODE_VOICE
;
7104 if(uData
->access
>= UL_PRESENT
)
7105 cData
->visited
= now
;
7106 if(cData
->user_greeting
)
7107 greeting
= cData
->user_greeting
;
7109 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7110 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7118 /* If user joining normally (not during burst), apply op or voice,
7119 * and send greeting/userinfo as appropriate.
7121 if(!user
->uplink
->burst
)
7125 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7126 if(modes & MODE_CHANOP) {
7127 modes &= ~MODE_HALFOP;
7128 modes &= ~MODE_VOICE;
7131 change
.args
[0].mode
= modes
;
7132 change
.args
[0].u
.member
= mNode
;
7133 mod_chanmode_announce(chanserv
, channel
, &change
);
7136 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7138 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7144 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7146 struct mod_chanmode change
;
7147 struct userData
*channel
;
7148 unsigned int ii
, jj
, i
;
7150 if(!user
->handle_info
)
7153 mod_chanmode_init(&change
);
7155 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7157 struct chanNode
*cn
;
7158 struct modeNode
*mn
;
7159 if(IsUserSuspended(channel
)
7160 || IsSuspended(channel
->channel
)
7161 || !(cn
= channel
->channel
->channel
))
7164 mn
= GetUserMode(cn
, user
);
7167 if(!IsUserSuspended(channel
)
7168 && IsUserAutoInvite(channel
)
7169 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7171 && !user
->uplink
->burst
)
7172 irc_invite(chanserv
, user
, cn
);
7176 if(channel
->access
>= UL_PRESENT
)
7177 channel
->channel
->visited
= now
;
7179 if(IsUserAutoOp(channel
))
7181 if(channel
->access
>= UL_OP
)
7182 change
.args
[0].mode
= MODE_CHANOP
;
7183 else if(channel
->access
>= UL_HALFOP
)
7184 change
.args
[0].mode
= MODE_HALFOP
;
7185 else if(channel
->access
>= UL_PEON
)
7186 change
.args
[0].mode
= MODE_VOICE
;
7188 change
.args
[0].mode
= 0;
7189 change
.args
[0].u
.member
= mn
;
7190 if(change
.args
[0].mode
)
7191 mod_chanmode_announce(chanserv
, cn
, &change
);
7194 channel
->seen
= now
;
7195 channel
->present
= 1;
7198 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7200 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7201 struct banData
*ban
;
7203 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7204 || !channel
->channel_info
7205 || IsSuspended(channel
->channel_info
))
7207 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7208 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7210 if(jj
< channel
->banlist
.used
)
7212 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7214 char kick_reason
[MAXLEN
];
7215 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7217 change
.args
[0].mode
= MODE_BAN
;
7218 change
.args
[0].u
.hostmask
= ban
->mask
;
7219 mod_chanmode_announce(chanserv
, channel
, &change
);
7220 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7221 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7222 ban
->triggered
= now
;
7227 if(IsSupportHelper(user
))
7229 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7231 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7233 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7239 if (user
->handle_info
->ignores
->used
) {
7240 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7241 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7245 if (user
->handle_info
->epithet
)
7246 irc_swhois(chanserv
, user
, user
->handle_info
->epithet
);
7250 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7252 struct chanData
*cData
;
7253 struct userData
*uData
;
7255 cData
= mn
->channel
->channel_info
;
7256 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7259 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7261 /* Allow for a bit of padding so that the limit doesn't
7262 track the user count exactly, which could get annoying. */
7263 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7265 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7266 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7270 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7272 scan_user_presence(uData
, mn
->user
);
7276 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7278 unsigned int ii
, jj
;
7279 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7281 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7282 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7284 if(jj
< mn
->user
->channels
.used
)
7287 if(ii
== chanserv_conf
.support_channels
.used
)
7288 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7293 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7295 struct userData
*uData
;
7297 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7298 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7299 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7302 if(protect_user(victim
, kicker
, channel
->channel_info
))
7304 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7305 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7308 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7313 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7315 struct chanData
*cData
;
7317 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7320 cData
= channel
->channel_info
;
7321 if(bad_topic(channel
, user
, channel
->topic
))
7322 { /* User doesnt have privs to set topics. Undo it */
7323 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7324 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7327 /* If there is a topic mask set, and the new topic doesnt match,
7328 * set the topic to mask + new_topic */
7329 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7331 char new_topic
[TOPICLEN
+1];
7332 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7335 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7336 /* and fall through to topicsnarf code below.. */
7338 else /* Topic couldnt fit into mask, was too long */
7340 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7341 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7342 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7346 /* With topicsnarf, grab the topic and save it as the default topic. */
7347 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7350 cData
->topic
= strdup(channel
->topic
);
7356 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7358 struct mod_chanmode
*bounce
= NULL
;
7359 unsigned int bnc
, ii
;
7362 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7365 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7366 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7368 char correct
[MAXLEN
];
7369 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7370 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7371 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7373 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7375 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7377 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7378 if(!protect_user(victim
, user
, channel
->channel_info
))
7381 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7384 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7385 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7386 if(bounce
->args
[bnc
].u
.member
)
7390 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7391 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7393 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7395 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7397 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7398 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7401 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7402 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7403 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7406 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7408 const char *ban
= change
->args
[ii
].u
.hostmask
;
7409 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7412 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7413 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7414 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7416 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7421 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7422 mod_chanmode_announce(chanserv
, channel
, bounce
);
7423 for(ii
= 0; ii
< change
->argc
; ++ii
)
7424 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7425 free((char*)bounce
->args
[ii
].u
.hostmask
);
7426 mod_chanmode_free(bounce
);
7431 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7433 struct chanNode
*channel
;
7434 struct banData
*bData
;
7435 struct mod_chanmode change
;
7436 unsigned int ii
, jj
;
7437 char kick_reason
[MAXLEN
];
7439 mod_chanmode_init(&change
);
7441 change
.args
[0].mode
= MODE_BAN
;
7442 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7444 channel
= user
->channels
.list
[ii
]->channel
;
7445 /* Need not check for bans if they're opped or voiced. */
7446 /* TODO: does this make sense in automode v, h, and o? *
7447 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7448 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7450 /* Need not check for bans unless channel registration is active. */
7451 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7453 /* Look for a matching ban already on the channel. */
7454 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7455 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7457 /* Need not act if we found one. */
7458 if(jj
< channel
->banlist
.used
)
7460 /* Look for a matching ban in this channel. */
7461 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7463 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7465 change
.args
[0].u
.hostmask
= bData
->mask
;
7466 mod_chanmode_announce(chanserv
, channel
, &change
);
7467 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7468 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7469 bData
->triggered
= now
;
7470 break; /* we don't need to check any more bans in the channel */
7475 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7477 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7481 dict_remove2(handle_dnrs
, old_handle
, 1);
7482 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7483 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7488 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7490 struct userNode
*h_user
;
7492 if(handle
->channels
)
7494 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7495 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7497 while(handle
->channels
)
7498 del_channel_user(handle
->channels
, 1);
7503 handle_server_link(UNUSED_ARG(struct server
*server
))
7505 struct chanData
*cData
;
7507 for(cData
= channelList
; cData
; cData
= cData
->next
)
7509 if(!IsSuspended(cData
))
7510 cData
->may_opchan
= 1;
7511 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7512 && !cData
->channel
->join_flooded
7513 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7514 < chanserv_conf
.adjust_threshold
))
7516 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7517 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7523 chanserv_conf_read(void)
7527 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7528 struct mod_chanmode
*change
;
7529 struct string_list
*strlist
;
7530 struct chanNode
*chan
;
7533 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7535 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7538 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7539 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7540 chanserv_conf
.support_channels
.used
= 0;
7541 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7543 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7545 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7548 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7550 channelList_append(&chanserv_conf
.support_channels
, chan
);
7553 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7556 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7559 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7561 channelList_append(&chanserv_conf
.support_channels
, chan
);
7563 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7564 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7565 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7566 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7567 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7568 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7569 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7570 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7571 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7572 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7573 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7574 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7575 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7576 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7577 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7578 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7579 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7580 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7581 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7582 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7583 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7584 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7585 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7586 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7587 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7589 NickChange(chanserv
, str
, 0);
7590 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7591 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7592 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7593 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7594 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7595 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7596 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7597 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7598 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7599 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7600 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7601 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7602 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7603 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7604 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7605 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7606 str
= database_get_data(conf_node
, KEY_GOD_TIMEOUT
, RECDB_QSTRING
);
7607 god_timeout
= str
? ParseInterval(str
) : 60*15;
7608 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7611 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7612 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7613 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7614 && (change
->argc
< 2))
7616 chanserv_conf
.default_modes
= *change
;
7617 mod_chanmode_free(change
);
7619 free_string_list(chanserv_conf
.set_shows
);
7620 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7622 strlist
= string_list_copy(strlist
);
7625 static const char *list
[] = {
7626 /* free form text */
7627 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7628 /* options based on user level */
7629 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7630 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7631 /* multiple choice options */
7632 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7633 /* binary options */
7634 "DynLimit", "NoDelete", "BanTimeout",
7639 strlist
= alloc_string_list(ArrayLength(list
)-1);
7640 for(ii
=0; list
[ii
]; ii
++)
7641 string_list_append(strlist
, strdup(list
[ii
]));
7643 chanserv_conf
.set_shows
= strlist
;
7644 /* We don't look things up now, in case the list refers to options
7645 * defined by modules initialized after this point. Just mark the
7646 * function list as invalid, so it will be initialized.
7648 set_shows_list
.used
= 0;
7649 free_string_list(chanserv_conf
.eightball
);
7650 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7653 strlist
= string_list_copy(strlist
);
7657 strlist
= alloc_string_list(4);
7658 string_list_append(strlist
, strdup("Yes."));
7659 string_list_append(strlist
, strdup("No."));
7660 string_list_append(strlist
, strdup("Maybe so."));
7662 chanserv_conf
.eightball
= strlist
;
7663 free_string_list(chanserv_conf
.old_ban_names
);
7664 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7666 strlist
= string_list_copy(strlist
);
7668 strlist
= alloc_string_list(2);
7669 chanserv_conf
.old_ban_names
= strlist
;
7670 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7671 off_channel
= str
? atoi(str
) : 0;
7675 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7678 struct note_type
*ntype
;
7681 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7683 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7686 if(!(ntype
= chanserv_create_note_type(key
)))
7688 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7692 /* Figure out set access */
7693 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7695 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7696 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7698 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7700 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7701 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7703 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7705 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7709 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7710 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7711 ntype
->set_access
.min_opserv
= 0;
7714 /* Figure out visibility */
7715 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7716 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7717 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7718 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7719 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7720 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7721 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7722 ntype
->visible_type
= NOTE_VIS_ALL
;
7724 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7726 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7727 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7731 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7733 struct handle_info
*handle
;
7734 struct userData
*uData
;
7735 char *seen
, *inf
, *flags
, *expires
;
7737 unsigned short access
;
7739 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7741 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7745 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7746 if(access
> UL_OWNER
)
7748 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7752 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7753 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7754 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7755 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7756 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7757 handle
= get_handle_info(key
);
7760 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7764 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7765 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7766 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7768 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7770 if(uData
->expires
> now
)
7771 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7773 uData
->flags
&= ~USER_SUSPENDED
;
7776 /* Upgrade: set autoop to the inverse of noautoop */
7777 if(chanserv_read_version
< 2)
7779 /* if noautoop is true, set autoop false, and vice versa */
7780 if(uData
->flags
& USER_NOAUTO_OP
)
7781 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7783 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7784 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
);
7790 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7792 struct banData
*bData
;
7793 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7794 time_t set_time
, triggered_time
, expires_time
;
7796 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7798 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7802 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7803 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7804 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7805 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7806 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7807 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7808 if (!reason
|| !owner
)
7811 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7812 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7814 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7816 expires_time
= set_time
+ atoi(s_duration
);
7820 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7823 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7826 static struct suspended
*
7827 chanserv_read_suspended(dict_t obj
)
7829 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7833 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7834 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7835 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7836 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7837 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7838 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7839 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7840 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7841 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7842 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7846 static struct giveownership
*
7847 chanserv_read_giveownership(dict_t obj
)
7849 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7853 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7854 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7856 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7858 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7859 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7861 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7862 giveownership
->reason
= str
? strdup(str
) : NULL
;
7863 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7864 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7866 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7867 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7868 return giveownership
;
7872 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7874 struct suspended
*suspended
;
7875 struct giveownership
*giveownership
;
7876 struct mod_chanmode
*modes
;
7877 struct chanNode
*cNode
;
7878 struct chanData
*cData
;
7879 struct dict
*channel
, *obj
;
7880 char *str
, *argv
[10];
7884 channel
= hir
->d
.object
;
7886 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7889 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7892 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7895 cData
= register_channel(cNode
, str
);
7898 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7902 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7904 enum levelOption lvlOpt
;
7905 enum charOption chOpt
;
7907 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7908 cData
->flags
= atoi(str
);
7910 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7912 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7914 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7915 else if(levelOptions
[lvlOpt
].old_flag
)
7917 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7918 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7920 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7924 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7926 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7928 cData
->chOpts
[chOpt
] = str
[0];
7931 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7933 enum levelOption lvlOpt
;
7934 enum charOption chOpt
;
7937 cData
->flags
= base64toint(str
, 5);
7938 count
= strlen(str
+= 5);
7939 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7942 if(levelOptions
[lvlOpt
].old_flag
)
7944 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7945 lvl
= levelOptions
[lvlOpt
].flag_value
;
7947 lvl
= levelOptions
[lvlOpt
].default_value
;
7949 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7951 case 'c': lvl
= UL_COOWNER
; break;
7952 case 'm': lvl
= UL_MANAGER
; break;
7953 case 'n': lvl
= UL_OWNER
+1; break;
7954 case 'o': lvl
= UL_OP
; break;
7955 case 'p': lvl
= UL_PEON
; break;
7956 case 'h': lvl
= UL_HALFOP
; break;
7957 case 'w': lvl
= UL_OWNER
; break;
7958 default: lvl
= 0; break;
7960 cData
->lvlOpts
[lvlOpt
] = lvl
;
7962 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7963 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7966 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7968 suspended
= chanserv_read_suspended(obj
);
7969 cData
->suspended
= suspended
;
7970 suspended
->cData
= cData
;
7971 /* We could use suspended->expires and suspended->revoked to
7972 * set the CHANNEL_SUSPENDED flag, but we don't. */
7974 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7976 suspended
= calloc(1, sizeof(*suspended
));
7977 suspended
->issued
= 0;
7978 suspended
->revoked
= 0;
7979 suspended
->suspender
= strdup(str
);
7980 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7981 suspended
->expires
= str
? atoi(str
) : 0;
7982 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7983 suspended
->reason
= strdup(str
? str
: "No reason");
7984 suspended
->previous
= NULL
;
7985 cData
->suspended
= suspended
;
7986 suspended
->cData
= cData
;
7990 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7991 suspended
= NULL
; /* to squelch a warning */
7994 if(IsSuspended(cData
)) {
7995 if(suspended
->expires
> now
)
7996 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7997 else if(suspended
->expires
)
7998 cData
->flags
&= ~CHANNEL_SUSPENDED
;
8001 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
8003 giveownership
= chanserv_read_giveownership(obj
);
8004 cData
->giveownership
= giveownership
;
8007 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
8008 struct mod_chanmode change
;
8009 mod_chanmode_init(&change
);
8011 change
.args
[0].mode
= MODE_CHANOP
;
8012 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
8013 mod_chanmode_announce(chanserv
, cNode
, &change
);
8016 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
8017 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8018 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
8019 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
8020 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
8021 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
8022 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
8023 cData
->max
= str
? atoi(str
) : 0;
8024 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
8025 cData
->greeting
= str
? strdup(str
) : NULL
;
8026 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
8027 cData
->user_greeting
= str
? strdup(str
) : NULL
;
8028 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
8029 cData
->topic_mask
= str
? strdup(str
) : NULL
;
8030 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
8031 cData
->topic
= str
? strdup(str
) : NULL
;
8033 if(!IsSuspended(cData
)
8034 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
8035 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
8036 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
8037 cData
->modes
= *modes
;
8039 cData
->modes
.modes_set
|= MODE_REGISTERED
;
8040 if(cData
->modes
.argc
> 1)
8041 cData
->modes
.argc
= 1;
8042 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
8043 mod_chanmode_free(modes
);
8046 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
8047 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8048 user_read_helper(iter_key(it
), iter_data(it
), cData
);
8050 if(!cData
->users
&& !IsProtected(cData
))
8052 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
8053 unregister_channel(cData
, "has empty user list.");
8057 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
8058 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8059 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
8061 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
8062 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
8064 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
8065 struct record_data
*rd
= iter_data(it
);
8066 const char *note
, *setter
;
8068 if(rd
->type
!= RECDB_OBJECT
)
8070 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
8074 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
8076 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
8078 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
8082 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
8083 if(!setter
) setter
= "<unknown>";
8084 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
8092 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
8094 const char *setter
, *reason
, *str
;
8095 struct do_not_register
*dnr
;
8097 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8100 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8103 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8106 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8109 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8112 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8114 dnr
->set
= atoi(str
);
8120 chanserv_version_read(struct dict
*section
)
8124 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8126 chanserv_read_version
= atoi(str
);
8127 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8131 chanserv_saxdb_read(struct dict
*database
)
8133 struct dict
*section
;
8136 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8137 chanserv_version_read(section
);
8139 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8140 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8141 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8143 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8144 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8145 chanserv_channel_read(iter_key(it
), iter_data(it
));
8147 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8148 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8149 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8155 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8157 int high_present
= 0;
8158 saxdb_start_record(ctx
, KEY_USERS
, 1);
8159 for(; uData
; uData
= uData
->next
)
8161 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8163 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8164 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8165 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8167 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8169 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8171 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8172 saxdb_end_record(ctx
);
8174 saxdb_end_record(ctx
);
8175 return high_present
;
8179 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8183 saxdb_start_record(ctx
, KEY_BANS
, 1);
8184 for(; bData
; bData
= bData
->next
)
8186 saxdb_start_record(ctx
, bData
->mask
, 0);
8187 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8188 if(bData
->triggered
)
8189 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8191 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8193 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8195 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8196 saxdb_end_record(ctx
);
8198 saxdb_end_record(ctx
);
8202 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8204 saxdb_start_record(ctx
, name
, 0);
8205 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8206 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8208 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8210 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8212 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8214 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8215 saxdb_end_record(ctx
);
8219 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8221 saxdb_start_record(ctx
, name
, 0);
8222 if(giveownership
->staff_issuer
)
8223 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8224 if(giveownership
->old_owner
)
8225 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8226 if(giveownership
->target
)
8227 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8228 if(giveownership
->target_access
)
8229 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8230 if(giveownership
->reason
)
8231 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8232 if(giveownership
->issued
)
8233 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8234 if(giveownership
->previous
)
8235 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8236 saxdb_end_record(ctx
);
8240 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8244 enum levelOption lvlOpt
;
8245 enum charOption chOpt
;
8247 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8249 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8250 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8252 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8253 if(channel
->registrar
)
8254 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8255 if(channel
->greeting
)
8256 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8257 if(channel
->user_greeting
)
8258 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8259 if(channel
->topic_mask
)
8260 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8261 if(channel
->suspended
)
8262 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8263 if(channel
->giveownership
)
8264 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8266 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8267 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8268 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8269 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8270 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8272 buf
[0] = channel
->chOpts
[chOpt
];
8274 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8276 saxdb_end_record(ctx
);
8278 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8280 mod_chanmode_format(&channel
->modes
, buf
);
8281 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8284 high_present
= chanserv_write_users(ctx
, channel
->users
);
8285 chanserv_write_bans(ctx
, channel
->bans
);
8287 if(dict_size(channel
->notes
))
8291 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8292 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8294 struct note
*note
= iter_data(it
);
8295 saxdb_start_record(ctx
, iter_key(it
), 0);
8296 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8297 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8298 saxdb_end_record(ctx
);
8300 saxdb_end_record(ctx
);
8303 if(channel
->ownerTransfer
)
8304 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8305 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8306 saxdb_end_record(ctx
);
8310 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8314 saxdb_start_record(ctx
, ntype
->name
, 0);
8315 switch(ntype
->set_access_type
)
8317 case NOTE_SET_CHANNEL_ACCESS
:
8318 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8320 case NOTE_SET_CHANNEL_SETTER
:
8321 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8323 case NOTE_SET_PRIVILEGED
: default:
8324 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8327 switch(ntype
->visible_type
)
8329 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8330 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8331 case NOTE_VIS_PRIVILEGED
:
8332 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8334 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8335 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8336 saxdb_end_record(ctx
);
8340 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8342 struct do_not_register
*dnr
;
8345 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8347 dnr
= iter_data(it
);
8348 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8350 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8351 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8352 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8353 saxdb_end_record(ctx
);
8358 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8361 struct chanData
*channel
;
8363 /* Version Control*/
8364 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8365 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8366 saxdb_end_record(ctx
);
8369 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8370 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8371 chanserv_write_note_type(ctx
, iter_data(it
));
8372 saxdb_end_record(ctx
);
8375 saxdb_start_record(ctx
, KEY_DNR
, 1);
8376 write_dnrs_helper(ctx
, handle_dnrs
);
8377 write_dnrs_helper(ctx
, plain_dnrs
);
8378 write_dnrs_helper(ctx
, mask_dnrs
);
8379 saxdb_end_record(ctx
);
8382 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8383 for(channel
= channelList
; channel
; channel
= channel
->next
)
8384 chanserv_write_channel(ctx
, channel
);
8385 saxdb_end_record(ctx
);
8391 chanserv_db_cleanup(void) {
8393 unreg_part_func(handle_part
);
8395 unregister_channel(channelList
, "terminating.");
8396 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8397 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8398 free(chanserv_conf
.support_channels
.list
);
8399 dict_delete(handle_dnrs
);
8400 dict_delete(plain_dnrs
);
8401 dict_delete(mask_dnrs
);
8402 dict_delete(note_types
);
8403 free_string_list(chanserv_conf
.eightball
);
8404 free_string_list(chanserv_conf
.old_ban_names
);
8405 free_string_list(chanserv_conf
.set_shows
);
8406 free(set_shows_list
.list
);
8407 free(uset_shows_list
.list
);
8410 struct userData
*helper
= helperList
;
8411 helperList
= helperList
->next
;
8416 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8417 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8418 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8421 init_chanserv(const char *nick
)
8423 struct chanNode
*chan
;
8425 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8426 conf_register_reload(chanserv_conf_read
);
8428 reg_server_link_func(handle_server_link
);
8430 reg_new_channel_func(handle_new_channel
);
8431 reg_join_func(handle_join
);
8432 reg_part_func(handle_part
);
8433 reg_kick_func(handle_kick
);
8434 reg_topic_func(handle_topic
);
8435 reg_mode_change_func(handle_mode
);
8436 reg_nick_change_func(handle_nick_change
);
8438 reg_auth_func(handle_auth
);
8439 reg_handle_rename_func(handle_rename
);
8440 reg_unreg_func(handle_unreg
);
8442 handle_dnrs
= dict_new();
8443 dict_set_free_data(handle_dnrs
, free
);
8444 plain_dnrs
= dict_new();
8445 dict_set_free_data(plain_dnrs
, free
);
8446 mask_dnrs
= dict_new();
8447 dict_set_free_data(mask_dnrs
, free
);
8449 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8450 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8451 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8452 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8453 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8454 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8455 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8456 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8457 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8458 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8460 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8462 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8463 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8465 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8466 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8467 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8468 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8469 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8471 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8472 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8473 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8474 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8475 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8476 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8478 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8479 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8480 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8481 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8483 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8484 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8485 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8486 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8487 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8488 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8489 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8490 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8491 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8492 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8494 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8495 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8496 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8497 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8498 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8499 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8500 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8501 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8502 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8503 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8504 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8505 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8506 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8507 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8509 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8510 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8511 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8512 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8514 /* if you change dellamer access, see also places
8515 * like unbanme which have manager hardcoded. */
8516 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8517 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8519 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8521 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8523 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8524 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8525 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8526 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8527 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8528 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8529 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8530 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8531 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8532 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8533 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8534 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8536 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8537 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8539 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8540 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8541 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8542 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8544 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8545 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8546 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8547 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8548 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8550 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8551 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8552 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8553 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8554 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8555 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8556 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8558 /* Channel options */
8559 DEFINE_CHANNEL_OPTION(defaulttopic
);
8560 DEFINE_CHANNEL_OPTION(topicmask
);
8561 DEFINE_CHANNEL_OPTION(greeting
);
8562 DEFINE_CHANNEL_OPTION(usergreeting
);
8563 DEFINE_CHANNEL_OPTION(modes
);
8564 DEFINE_CHANNEL_OPTION(enfops
);
8565 DEFINE_CHANNEL_OPTION(enfhalfops
);
8566 DEFINE_CHANNEL_OPTION(automode
);
8567 DEFINE_CHANNEL_OPTION(protect
);
8568 DEFINE_CHANNEL_OPTION(enfmodes
);
8569 DEFINE_CHANNEL_OPTION(enftopic
);
8570 DEFINE_CHANNEL_OPTION(pubcmd
);
8571 DEFINE_CHANNEL_OPTION(userinfo
);
8572 DEFINE_CHANNEL_OPTION(dynlimit
);
8573 DEFINE_CHANNEL_OPTION(topicsnarf
);
8574 DEFINE_CHANNEL_OPTION(nodelete
);
8575 DEFINE_CHANNEL_OPTION(toys
);
8576 DEFINE_CHANNEL_OPTION(setters
);
8577 DEFINE_CHANNEL_OPTION(topicrefresh
);
8578 DEFINE_CHANNEL_OPTION(resync
);
8579 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8580 DEFINE_CHANNEL_OPTION(bantimeout
);
8581 DEFINE_CHANNEL_OPTION(inviteme
);
8583 DEFINE_CHANNEL_OPTION(offchannel
);
8584 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8586 /* Alias set topic to set defaulttopic for compatibility. */
8587 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8590 DEFINE_USER_OPTION(autoinvite
);
8591 DEFINE_USER_OPTION(info
);
8592 DEFINE_USER_OPTION(autoop
);
8594 /* Alias uset autovoice to uset autoop. */
8595 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8597 note_types
= dict_new();
8598 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8601 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8602 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8603 service_register(chanserv
)->trigger
= '!';
8604 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8607 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8609 if(chanserv_conf
.channel_expire_frequency
)
8610 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8612 if(chanserv_conf
.ban_timeout_frequency
)
8613 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8615 if(chanserv_conf
.refresh_period
)
8617 time_t next_refresh
;
8618 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8619 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8620 timeq_add(next_refresh
, chanserv_auto_resync
, NULL
);
8623 if (autojoin_channels
&& chanserv
) {
8624 for (i
= 0; i
< autojoin_channels
->used
; i
++) {
8625 chan
= AddChannel(autojoin_channels
->list
[i
], now
, "+nt", NULL
, NULL
);
8626 AddChannelUser(chanserv
, chan
)->modes
|= MODE_CHANOP
;
8630 reg_exit_func(chanserv_db_cleanup
);
8631 message_register_table(msgtab
);