1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * srvx 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() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_VERSION_CONTROL "version_control"
61 #define KEY_CHANNELS "channels"
62 #define KEY_NOTE_TYPES "note_types"
64 /* version control paramiter */
65 #define KEY_VERSION_NUMBER "version_number"
67 /* Note type parameters */
68 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
69 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
70 #define KEY_NOTE_SETTER_ACCESS "setter_access"
71 #define KEY_NOTE_VISIBILITY "visibility"
72 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
73 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
74 #define KEY_NOTE_VIS_ALL "all"
75 #define KEY_NOTE_MAX_LENGTH "max_length"
76 #define KEY_NOTE_SETTER "setter"
77 #define KEY_NOTE_NOTE "note"
79 /* Do-not-register channels */
81 #define KEY_DNR_SET "set"
82 #define KEY_DNR_SETTER "setter"
83 #define KEY_DNR_REASON "reason"
86 #define KEY_REGISTERED "registered"
87 #define KEY_REGISTRAR "registrar"
88 #define KEY_SUSPENDED "suspended"
89 #define KEY_PREVIOUS "previous"
90 #define KEY_SUSPENDER "suspender"
91 #define KEY_ISSUED "issued"
92 #define KEY_REVOKED "revoked"
93 #define KEY_SUSPEND_EXPIRES "suspend_expires"
94 #define KEY_SUSPEND_REASON "suspend_reason"
95 #define KEY_VISITED "visited"
96 #define KEY_TOPIC "topic"
97 #define KEY_GREETING "greeting"
98 #define KEY_USER_GREETING "user_greeting"
99 #define KEY_MODES "modes"
100 #define KEY_FLAGS "flags"
101 #define KEY_OPTIONS "options"
102 #define KEY_USERS "users"
103 #define KEY_BANS "bans" /* for lamers */
104 #define KEY_MAX "max"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
110 #define KEY_LEVEL "level"
111 #define KEY_INFO "info"
112 #define KEY_SEEN "seen"
115 #define KEY_OWNER "owner"
116 #define KEY_REASON "reason"
117 #define KEY_SET "set"
118 #define KEY_DURATION "duration"
119 #define KEY_EXPIRES "expires"
120 #define KEY_TRIGGERED "triggered"
122 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
123 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
125 /* Administrative messages */
126 static const struct message_entry msgtab
[] = {
127 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
129 /* Channel registration */
130 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
131 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
132 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
133 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
134 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
135 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
136 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
137 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
139 /* Do-not-register channels */
140 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
141 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
142 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
143 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
144 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
145 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
146 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
147 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
148 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
149 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
150 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
152 /* Channel unregistration */
153 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
154 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
155 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
156 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
159 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
160 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
162 /* Channel merging */
163 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
164 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
165 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
166 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
167 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
169 /* Handle unregistration */
170 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
173 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
174 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
175 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
176 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
177 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
178 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
179 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
180 { "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." },
181 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
182 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
183 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
184 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
185 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
186 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
188 /* Removing yourself from a channel. */
189 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
190 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
191 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
193 /* User management */
194 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
195 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
196 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
197 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
198 { "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." },
199 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
200 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
201 { "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." },
202 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
203 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
204 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
205 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
206 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
207 { "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" },
208 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
210 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
211 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
212 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
213 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
214 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
215 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
218 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
219 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
220 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
221 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
222 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
223 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
224 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
225 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
226 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
227 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
228 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
229 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
230 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
231 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
232 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
233 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
234 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
236 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
238 /* Channel management */
239 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
240 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
241 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
243 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
244 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
245 { "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" },
246 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
247 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
248 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
249 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
251 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
252 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
253 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
254 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
255 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
256 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
257 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
258 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
259 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
261 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
262 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
263 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
265 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
266 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
267 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
268 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
269 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
270 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
271 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
272 { "CSMSG_SET_MODES", "$bModes $b %s" },
273 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
274 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
275 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
276 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
278 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
279 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
283 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
284 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
286 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
288 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
289 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
290 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
291 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
292 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
293 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
294 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
295 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
296 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
297 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
298 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
299 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
300 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
305 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
306 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
307 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
308 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
309 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
312 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
313 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
314 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
315 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
316 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
317 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
318 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
319 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
320 { "CSMSG_PROTECT_NONE", "No users will be protected." },
321 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
322 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
323 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
325 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
326 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
327 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
328 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
329 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
331 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
332 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
333 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
334 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
335 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
337 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
338 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
339 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
340 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
341 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
342 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
344 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
345 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
346 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
347 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
348 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
349 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
350 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
351 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
353 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
354 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
355 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
357 /* Channel userlist */
358 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
359 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
360 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
361 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
362 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
364 /* Channel note list */
365 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
366 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
367 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
368 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
369 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
370 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
371 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
372 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
373 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
374 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
375 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
376 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
377 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
378 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
379 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
381 /* Channel [un]suspension */
382 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
383 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
384 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
385 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
386 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
387 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
388 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
390 /* Access information */
391 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
392 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
393 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
394 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
395 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
396 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
397 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
398 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
399 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
400 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
401 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
403 /* Seen information */
404 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
405 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
406 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
407 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
409 /* Names information */
410 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
411 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
413 /* Channel information */
414 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
415 { "CSMSG_BAR", "----------------------------------------"},
416 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
417 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
418 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
419 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
420 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
421 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
422 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
423 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
424 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
425 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
426 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
427 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
428 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
434 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
435 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
436 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
438 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
439 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
440 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
441 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
442 { "CSMSG_PEEK_OPS", "$bOps:$b" },
443 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
444 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
446 /* Network information */
447 { "CSMSG_NETWORK_INFO", "Network Information:" },
448 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
449 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
450 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
451 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
452 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
453 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
454 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
455 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
458 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
459 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
460 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
462 /* Channel searches */
463 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
464 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
465 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
466 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
468 /* Channel configuration */
469 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
470 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
471 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
472 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
475 { "CSMSG_USER_OPTIONS", "User Options:" },
476 // { "CSMSG_USER_PROTECTED", "That user is protected." },
479 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
480 { "CSMSG_PING_RESPONSE", "Pong!" },
481 { "CSMSG_WUT_RESPONSE", "wut" },
482 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
483 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
484 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
485 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
486 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
487 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
488 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
491 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
492 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
496 /* eject_user and unban_user flags */
497 #define ACTION_KICK 0x0001
498 #define ACTION_BAN 0x0002
499 #define ACTION_ADD_LAMER 0x0004
500 #define ACTION_ADD_TIMED_LAMER 0x0008
501 #define ACTION_UNBAN 0x0010
502 #define ACTION_DEL_LAMER 0x0020
504 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
505 #define MODELEN 40 + KEYLEN
509 #define CSFUNC_ARGS user, channel, argc, argv, cmd
511 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
512 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
513 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
514 reply("MSG_MISSING_PARAMS", argv[0]); \
518 DECLARE_LIST(dnrList
, struct do_not_register
*);
519 DEFINE_LIST(dnrList
, struct do_not_register
*);
521 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
523 struct userNode
*chanserv
;
526 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
527 static struct log_type
*CS_LOG
;
528 struct adduserPending
* adduser_pendings
= NULL
;
529 unsigned int adduser_pendings_count
= 0;
533 struct channelList support_channels
;
534 struct mod_chanmode default_modes
;
536 unsigned long db_backup_frequency
;
537 unsigned long channel_expire_frequency
;
538 unsigned long ban_timeout_frequency
;
541 unsigned int adjust_delay
;
542 long channel_expire_delay
;
543 unsigned int nodelete_level
;
545 unsigned int adjust_threshold
;
546 int join_flood_threshold
;
548 unsigned int greeting_length
;
549 unsigned int refresh_period
;
550 unsigned int giveownership_period
;
552 unsigned int max_owned
;
553 unsigned int max_chan_users
;
554 unsigned int max_chan_bans
; /* lamers */
555 unsigned int max_userinfo_length
;
557 struct string_list
*set_shows
;
558 struct string_list
*eightball
;
559 struct string_list
*old_ban_names
;
561 const char *ctcp_short_ban_duration
;
562 const char *ctcp_long_ban_duration
;
564 const char *irc_operator_epithet
;
565 const char *network_helper_epithet
;
566 const char *support_helper_epithet
;
571 struct userNode
*user
;
572 struct userNode
*bot
;
573 struct chanNode
*channel
;
575 unsigned short lowest
;
576 unsigned short highest
;
577 struct userData
**users
;
578 struct helpfile_table table
;
581 enum note_access_type
583 NOTE_SET_CHANNEL_ACCESS
,
584 NOTE_SET_CHANNEL_SETTER
,
588 enum note_visible_type
591 NOTE_VIS_CHANNEL_USERS
,
597 enum note_access_type set_access_type
;
599 unsigned int min_opserv
;
600 unsigned short min_ulevel
;
602 enum note_visible_type visible_type
;
603 unsigned int max_length
;
610 struct note_type
*type
;
611 char setter
[NICKSERV_HANDLE_LEN
+1];
615 static unsigned int registered_channels
;
616 static unsigned int banCount
;
618 static const struct {
621 unsigned short level
;
623 } accessLevels
[] = { /* MUST be orderd less to most! */
624 { "peon", "Peon", UL_PEON
, '+' },
625 { "halfop", "HalfOp", UL_HALFOP
, '%' },
626 { "op", "Op", UL_OP
, '@' },
627 { "manager", "Manager", UL_MANAGER
, '%' },
628 { "coowner", "Coowner", UL_COOWNER
, '*' },
629 { "owner", "Owner", UL_OWNER
, '!' },
630 { "helper", "BUG:", UL_HELPER
, 'X' }
633 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
634 static const struct {
637 unsigned short default_value
;
638 unsigned int old_idx
;
639 unsigned int old_flag
;
640 unsigned short flag_value
;
642 // { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
643 // { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL, 0 },
644 // { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 }, // these 3 need removed, but causes segs if its still in the db..
645 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
646 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
647 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
648 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
649 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
650 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
651 // { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
652 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
653 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
654 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
657 struct charOptionValues
{
661 { 'n', "CSMSG_VOICE_NONE" },
662 { 'p', "CSMSG_VOICE_PEON" },
663 { 'a', "CSMSG_VOICE_ALL" }
664 }, protectValues
[] = {
665 { 'a', "CSMSG_PROTECT_ALL" },
666 { 'e', "CSMSG_PROTECT_EQUAL" },
667 { 'l', "CSMSG_PROTECT_LOWER" },
668 { 'n', "CSMSG_PROTECT_NONE" }
670 { 'd', "CSMSG_TOYS_DISABLED" },
671 { 'n', "CSMSG_TOYS_PRIVATE" },
672 { 'p', "CSMSG_TOYS_PUBLIC" }
673 }, topicRefreshValues
[] = {
674 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
675 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
676 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
677 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
678 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
679 }, ctcpReactionValues
[] = {
680 { 'n', "CSMSG_CTCPREACTION_NONE" },
681 { 'k', "CSMSG_CTCPREACTION_KICK" },
682 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
683 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
684 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
685 }, banTimeoutValues
[] = {
686 { '0', "CSMSG_BANTIMEOUT_NONE" },
687 { '1', "CSMSG_BANTIMEOUT_10M" },
688 { '2', "CSMSG_BANTIMEOUT_2H" },
689 { '3', "CSMSG_BANTIMEOUT_4H" },
690 { '4', "CSMSG_BANTIMEOUT_1D" },
691 { '5', "CSMSG_BANTIMEOUT_1W" }
694 static const struct {
698 unsigned int old_idx
;
700 struct charOptionValues
*values
;
702 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues
), voiceValues
},
703 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
704 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
705 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
706 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
707 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
}
710 struct userData
*helperList
;
711 struct chanData
*channelList
;
712 static struct module *chanserv_module
;
713 static unsigned int userCount
;
714 unsigned int chanserv_read_version
= 0; /* db version control */
716 #define CHANSERV_DB_VERSION 2
718 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
719 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
720 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
723 user_level_from_name(const char *name
, unsigned short clamp_level
)
725 unsigned int level
= 0, ii
;
727 level
= strtoul(name
, NULL
, 10);
728 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
729 if(!irccasecmp(name
, accessLevels
[ii
].name
))
730 level
= accessLevels
[ii
].level
;
731 if(level
> clamp_level
)
737 user_level_name_from_level(int level
)
745 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
746 if(level
>= accessLevels
[ii
].level
)
747 highest
= accessLevels
[ii
].title
;
753 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
756 *minl
= strtoul(arg
, &sep
, 10);
764 *maxl
= strtoul(sep
+1, &sep
, 10);
772 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
774 struct userData
*uData
, **head
;
776 if(!channel
|| !handle
)
779 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
780 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
782 for(uData
= helperList
;
783 uData
&& uData
->handle
!= handle
;
784 uData
= uData
->next
);
788 uData
= calloc(1, sizeof(struct userData
));
789 uData
->handle
= handle
;
791 uData
->access
= UL_HELPER
;
797 uData
->next
= helperList
;
799 helperList
->prev
= uData
;
807 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
808 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
811 head
= &(channel
->users
);
814 if(uData
&& (uData
!= *head
))
816 /* Shuffle the user to the head of whatever list he was in. */
818 uData
->next
->prev
= uData
->prev
;
820 uData
->prev
->next
= uData
->next
;
826 (**head
).prev
= uData
;
833 /* Returns non-zero if user has at least the minimum access.
834 * exempt_owner is set when handling !set, so the owner can set things
837 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
839 struct userData
*uData
;
840 struct chanData
*cData
= channel
->channel_info
;
841 unsigned short minimum
= cData
->lvlOpts
[opt
];
844 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
847 if(minimum
<= uData
->access
)
849 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
854 /* Scan for other users authenticated to the same handle
855 still in the channel. If so, keep them listed as present.
857 user is optional, if not null, it skips checking that userNode
858 (for the handle_part function) */
860 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
864 if(IsSuspended(uData
->channel
)
865 || IsUserSuspended(uData
)
866 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
878 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
880 unsigned int eflags
, argc
;
882 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
884 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
885 if(!channel
->channel_info
886 || IsSuspended(channel
->channel_info
)
888 || !ircncasecmp(text
, "ACTION ", 7))
890 /* We dont punish people we know -Rubin
891 * * Figure out the minimum level needed to CTCP the channel *
893 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
896 /* If they are a user of the channel, they are exempt */
897 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
899 /* We need to enforce against them; do so. */
902 argv
[1] = user
->nick
;
904 if(GetUserMode(channel
, user
))
905 eflags
|= ACTION_KICK
;
906 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
907 default: case 'n': return;
909 eflags
|= ACTION_KICK
;
912 eflags
|= ACTION_BAN
;
915 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
916 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
919 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
920 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
923 argv
[argc
++] = bad_ctcp_reason
;
924 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
928 chanserv_create_note_type(const char *name
)
930 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
931 strcpy(ntype
->name
, name
);
933 dict_insert(note_types
, ntype
->name
, ntype
);
938 chanserv_deref_note_type(void *data
)
940 struct note_type
*ntype
= data
;
942 if(--ntype
->refs
> 0)
948 chanserv_flush_note_type(struct note_type
*ntype
)
950 struct chanData
*cData
;
951 for(cData
= channelList
; cData
; cData
= cData
->next
)
952 dict_remove(cData
->notes
, ntype
->name
);
956 chanserv_truncate_notes(struct note_type
*ntype
)
958 struct chanData
*cData
;
960 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
962 for(cData
= channelList
; cData
; cData
= cData
->next
) {
963 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
966 if(strlen(note
->note
) <= ntype
->max_length
)
968 dict_remove2(cData
->notes
, ntype
->name
, 1);
969 note
= realloc(note
, size
);
970 note
->note
[ntype
->max_length
] = 0;
971 dict_insert(cData
->notes
, ntype
->name
, note
);
975 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
978 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
981 unsigned int len
= strlen(text
);
983 if(len
> type
->max_length
) len
= type
->max_length
;
984 note
= calloc(1, sizeof(*note
) + len
);
986 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
987 memcpy(note
->note
, text
, len
);
989 dict_insert(channel
->notes
, type
->name
, note
);
995 chanserv_free_note(void *data
)
997 struct note
*note
= data
;
999 chanserv_deref_note_type(note
->type
);
1000 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1004 static MODCMD_FUNC(cmd_createnote
) {
1005 struct note_type
*ntype
;
1006 unsigned int arg
= 1, existed
= 0, max_length
;
1008 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1011 ntype
= chanserv_create_note_type(argv
[arg
]);
1012 if(!irccasecmp(argv
[++arg
], "privileged"))
1015 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1016 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1018 else if(!irccasecmp(argv
[arg
], "channel"))
1020 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1023 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1026 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1027 ntype
->set_access
.min_ulevel
= ulvl
;
1029 else if(!irccasecmp(argv
[arg
], "setter"))
1031 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1035 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1039 if(!irccasecmp(argv
[++arg
], "privileged"))
1040 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1041 else if(!irccasecmp(argv
[arg
], "channel_users"))
1042 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1043 else if(!irccasecmp(argv
[arg
], "all"))
1044 ntype
->visible_type
= NOTE_VIS_ALL
;
1046 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1050 if((arg
+1) >= argc
) {
1051 reply("MSG_MISSING_PARAMS", argv
[0]);
1054 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1055 if(max_length
< 20 || max_length
> 450)
1057 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1060 if(existed
&& (max_length
< ntype
->max_length
))
1062 ntype
->max_length
= max_length
;
1063 chanserv_truncate_notes(ntype
);
1065 ntype
->max_length
= max_length
;
1068 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1070 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1075 dict_remove(note_types
, ntype
->name
);
1079 static MODCMD_FUNC(cmd_removenote
) {
1080 struct note_type
*ntype
;
1083 ntype
= dict_find(note_types
, argv
[1], NULL
);
1084 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1087 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1094 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1097 chanserv_flush_note_type(ntype
);
1099 dict_remove(note_types
, argv
[1]);
1100 reply("CSMSG_NOTE_DELETED", argv
[1]);
1105 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1109 if(orig
->modes_set
& change
->modes_clear
)
1111 if(orig
->modes_clear
& change
->modes_set
)
1113 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1114 && strcmp(orig
->new_key
, change
->new_key
))
1116 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1117 && (orig
->new_limit
!= change
->new_limit
))
1122 static char max_length_text
[MAXLEN
+1][16];
1124 static struct helpfile_expansion
1125 chanserv_expand_variable(const char *variable
)
1127 struct helpfile_expansion exp
;
1129 if(!irccasecmp(variable
, "notes"))
1132 exp
.type
= HF_TABLE
;
1133 exp
.value
.table
.length
= 1;
1134 exp
.value
.table
.width
= 3;
1135 exp
.value
.table
.flags
= 0;
1136 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1137 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1138 exp
.value
.table
.contents
[0][0] = "Note Type";
1139 exp
.value
.table
.contents
[0][1] = "Visibility";
1140 exp
.value
.table
.contents
[0][2] = "Max Length";
1141 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1143 struct note_type
*ntype
= iter_data(it
);
1146 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1147 row
= exp
.value
.table
.length
++;
1148 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1149 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1150 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1151 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1153 if(!max_length_text
[ntype
->max_length
][0])
1154 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1155 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1160 exp
.type
= HF_STRING
;
1161 exp
.value
.str
= NULL
;
1165 static struct chanData
*
1166 register_channel(struct chanNode
*cNode
, char *registrar
)
1168 struct chanData
*channel
;
1169 enum levelOption lvlOpt
;
1170 enum charOption chOpt
;
1172 channel
= calloc(1, sizeof(struct chanData
));
1174 channel
->notes
= dict_new();
1175 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1177 channel
->registrar
= strdup(registrar
);
1178 channel
->registered
= now
;
1179 channel
->visited
= now
;
1180 channel
->limitAdjusted
= now
;
1181 channel
->ownerTransfer
= now
;
1182 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1183 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1184 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1185 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1186 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1188 channel
->prev
= NULL
;
1189 channel
->next
= channelList
;
1192 channelList
->prev
= channel
;
1193 channelList
= channel
;
1194 registered_channels
++;
1196 channel
->channel
= cNode
;
1198 cNode
->channel_info
= channel
;
1203 static struct userData
*
1204 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1206 struct userData
*ud
;
1208 if(access
> UL_OWNER
)
1211 ud
= calloc(1, sizeof(*ud
));
1212 ud
->channel
= channel
;
1213 ud
->handle
= handle
;
1215 ud
->access
= access
;
1216 ud
->info
= info
? strdup(info
) : NULL
;
1219 ud
->next
= channel
->users
;
1221 channel
->users
->prev
= ud
;
1222 channel
->users
= ud
;
1224 channel
->userCount
++;
1228 ud
->u_next
= ud
->handle
->channels
;
1230 ud
->u_next
->u_prev
= ud
;
1231 ud
->handle
->channels
= ud
;
1233 ud
->flags
= USER_FLAGS_DEFAULT
;
1237 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1240 del_channel_user(struct userData
*user
, int do_gc
)
1242 struct chanData
*channel
= user
->channel
;
1244 channel
->userCount
--;
1248 user
->prev
->next
= user
->next
;
1250 channel
->users
= user
->next
;
1252 user
->next
->prev
= user
->prev
;
1255 user
->u_prev
->u_next
= user
->u_next
;
1257 user
->handle
->channels
= user
->u_next
;
1259 user
->u_next
->u_prev
= user
->u_prev
;
1263 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1264 unregister_channel(channel
, "lost all users.");
1267 static struct adduserPending
*
1268 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1270 struct adduserPending
*ap
;
1271 ap
= calloc(1,sizeof(struct adduserPending
));
1272 ap
->channel
= channel
;
1275 ap
->created
= time(NULL
);
1277 /* ap->prev defaults to NULL already.. */
1278 ap
->next
= adduser_pendings
;
1279 if(adduser_pendings
)
1280 adduser_pendings
->prev
= ap
;
1281 adduser_pendings
= ap
;
1282 adduser_pendings_count
++;
1287 del_adduser_pending(struct adduserPending
*ap
)
1290 ap
->prev
->next
= ap
->next
;
1292 adduser_pendings
= ap
->next
;
1295 ap
->next
->prev
= ap
->prev
;
1299 static void expire_adduser_pending();
1301 /* find_adduser_pending(channel, user) will find an arbitrary record
1302 * from user, channel, or user and channel.
1303 * if user or channel are NULL, they will match any records.
1305 static struct adduserPending
*
1306 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1308 struct adduserPending
*ap
;
1310 expire_adduser_pending(); /* why not here.. */
1312 if(!channel
&& !user
) /* 2 nulls matches all */
1313 return(adduser_pendings
);
1314 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1316 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1323 /* Remove all pendings for a user or channel
1325 * called in nickserv.c DelUser() and proto-* unregister_channel()
1328 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1330 struct adduserPending
*ap
;
1332 /* So this is a bit wastefull, i hate dealing with linked lists.
1333 * if its a problem we'll rewrite it right */
1334 while((ap
= find_adduser_pending(channel
, user
))) {
1335 del_adduser_pending(ap
);
1339 /* Called from nickserv.c cmd_auth after someone auths */
1341 process_adduser_pending(struct userNode
*user
)
1343 struct adduserPending
*ap
;
1344 if(!user
->handle_info
)
1345 return; /* not associated with an account */
1346 while((ap
= find_adduser_pending(NULL
, user
)))
1348 struct userData
*actee
;
1349 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1351 /* Already on the userlist. do nothing*/
1355 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1356 scan_user_presence(actee
, NULL
);
1358 del_adduser_pending(ap
);
1363 expire_adduser_pending()
1365 struct adduserPending
*ap
, *ap_next
;
1366 ap
= adduser_pendings
;
1369 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1371 ap_next
= ap
->next
; /* save next */
1372 del_adduser_pending(ap
); /* free and relink */
1373 ap
= ap_next
; /* advance */
1380 static void expire_ban(void *data
);
1382 static struct banData
*
1383 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1386 unsigned int ii
, l1
, l2
;
1391 bd
= malloc(sizeof(struct banData
));
1393 bd
->channel
= channel
;
1395 bd
->triggered
= triggered
;
1396 bd
->expires
= expires
;
1398 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1400 extern const char *hidden_host_suffix
;
1401 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1405 l2
= strlen(old_name
);
1408 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1410 new_mask
= alloca(MAXLEN
);
1411 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1414 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1416 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1417 bd
->reason
= strdup(reason
);
1420 timeq_add(expires
, expire_ban
, bd
);
1423 bd
->next
= channel
->bans
; /* lamers */
1425 channel
->bans
->prev
= bd
;
1427 channel
->banCount
++;
1434 del_channel_ban(struct banData
*ban
)
1436 ban
->channel
->banCount
--;
1440 ban
->prev
->next
= ban
->next
;
1442 ban
->channel
->bans
= ban
->next
;
1445 ban
->next
->prev
= ban
->prev
;
1448 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1457 expire_ban(void *data
) /* lamer.. */
1459 struct banData
*bd
= data
;
1460 if(!IsSuspended(bd
->channel
))
1462 struct banList bans
;
1463 struct mod_chanmode change
;
1465 bans
= bd
->channel
->channel
->banlist
;
1466 mod_chanmode_init(&change
);
1467 for(ii
=0; ii
<bans
.used
; ii
++)
1469 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1472 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1473 change
.args
[0].u
.hostmask
= bd
->mask
;
1474 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1480 del_channel_ban(bd
);
1483 static void chanserv_expire_suspension(void *data
);
1486 unregister_channel(struct chanData
*channel
, const char *reason
)
1488 struct mod_chanmode change
;
1489 char msgbuf
[MAXLEN
];
1491 /* After channel unregistration, the following must be cleaned
1493 - Channel information.
1495 - Channel bans. (lamers)
1496 - Channel suspension data.
1497 - adduser_pending data.
1498 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1504 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1508 mod_chanmode_init(&change
);
1509 change
.modes_clear
|= MODE_REGISTERED
;
1510 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1513 wipe_adduser_pending(channel
->channel
, NULL
);
1515 while(channel
->users
)
1516 del_channel_user(channel
->users
, 0);
1518 while(channel
->bans
)
1519 del_channel_ban(channel
->bans
);
1521 free(channel
->topic
);
1522 free(channel
->registrar
);
1523 free(channel
->greeting
);
1524 free(channel
->user_greeting
);
1525 free(channel
->topic_mask
);
1528 channel
->prev
->next
= channel
->next
;
1530 channelList
= channel
->next
;
1533 channel
->next
->prev
= channel
->prev
;
1535 if(channel
->suspended
)
1537 struct chanNode
*cNode
= channel
->channel
;
1538 struct suspended
*suspended
, *next_suspended
;
1540 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1542 next_suspended
= suspended
->previous
;
1543 free(suspended
->suspender
);
1544 free(suspended
->reason
);
1545 if(suspended
->expires
)
1546 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1551 cNode
->channel_info
= NULL
;
1553 channel
->channel
->channel_info
= NULL
;
1555 dict_delete(channel
->notes
);
1556 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1557 if(!IsSuspended(channel
))
1558 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1559 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1560 UnlockChannel(channel
->channel
);
1562 registered_channels
--;
1566 expire_channels(UNUSED_ARG(void *data
))
1568 struct chanData
*channel
, *next
;
1569 struct userData
*user
;
1570 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1572 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1573 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1575 for(channel
= channelList
; channel
; channel
= next
)
1577 next
= channel
->next
;
1579 /* See if the channel can be expired. */
1580 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1581 || IsProtected(channel
))
1584 /* Make sure there are no high-ranking users still in the channel. */
1585 for(user
=channel
->users
; user
; user
=user
->next
)
1586 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1591 /* Unregister the channel */
1592 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1593 unregister_channel(channel
, "registration expired.");
1596 if(chanserv_conf
.channel_expire_frequency
)
1597 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1601 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1603 char protect
= channel
->chOpts
[chProtect
];
1604 struct userData
*cs_victim
, *cs_aggressor
;
1606 /* Don't protect if no one is to be protected, someone is attacking
1607 himself, or if the aggressor is an IRC Operator. */
1608 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1611 /* Don't protect if the victim isn't authenticated (because they
1612 can't be a channel user), unless we are to protect non-users
1614 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1615 if(protect
!= 'a' && !cs_victim
)
1618 /* Protect if the aggressor isn't a user because at this point,
1619 the aggressor can only be less than or equal to the victim. */
1620 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1624 /* If the aggressor was a user, then the victim can't be helped. */
1631 if(cs_victim
->access
> cs_aggressor
->access
)
1636 if(cs_victim
->access
>= cs_aggressor
->access
)
1645 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1647 struct chanData
*cData
= channel
->channel_info
;
1648 struct userData
*cs_victim
;
1650 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1651 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1652 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1654 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1662 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1664 struct chanData
*cData
= channel
->channel_info
;
1665 struct userData
*cs_victim
;
1667 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1668 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1669 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1671 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1680 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1682 if(IsService(victim
))
1684 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1688 if(protect_user(victim
, user
, channel
->channel_info
))
1690 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1698 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1700 if(IsService(victim
))
1702 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1706 if(protect_user(victim
, user
, channel
->channel_info
))
1708 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1715 static struct do_not_register
*
1716 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1718 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1719 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1720 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1721 strcpy(dnr
->reason
, reason
);
1723 if(dnr
->chan_name
[0] == '*')
1724 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1725 else if(strpbrk(dnr
->chan_name
, "*?"))
1726 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1728 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1732 static struct dnrList
1733 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1735 struct dnrList list
;
1737 struct do_not_register
*dnr
;
1739 dnrList_init(&list
);
1740 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1741 dnrList_append(&list
, dnr
);
1742 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1743 dnrList_append(&list
, dnr
);
1745 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1746 if(match_ircglob(chan_name
, iter_key(it
)))
1747 dnrList_append(&list
, iter_data(it
));
1752 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1754 struct dnrList list
;
1755 struct do_not_register
*dnr
;
1757 char buf
[INTERVALLEN
];
1759 list
= chanserv_find_dnrs(chan_name
, handle
);
1760 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1762 dnr
= list
.list
[ii
];
1765 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1766 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1769 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1772 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1777 struct do_not_register
*
1778 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1780 struct do_not_register
*dnr
;
1783 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1787 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1789 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1790 if(match_ircglob(chan_name
, iter_key(it
)))
1791 return iter_data(it
);
1796 static CHANSERV_FUNC(cmd_noregister
)
1799 struct do_not_register
*dnr
;
1800 char buf
[INTERVALLEN
];
1801 unsigned int matches
;
1807 reply("CSMSG_DNR_SEARCH_RESULTS");
1810 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1812 dnr
= iter_data(it
);
1814 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1816 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1819 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1821 dnr
= iter_data(it
);
1823 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1825 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1828 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1830 dnr
= iter_data(it
);
1832 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1834 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1839 reply("MSG_MATCH_COUNT", matches
);
1841 reply("MSG_NO_MATCHES");
1847 if(!IsChannelName(target
) && (*target
!= '*'))
1849 reply("CSMSG_NOT_DNR", target
);
1855 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1856 if((*target
== '*') && !get_handle_info(target
+ 1))
1858 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1861 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1862 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1866 reply("CSMSG_DNR_SEARCH_RESULTS");
1869 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1871 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1873 reply("MSG_NO_MATCHES");
1877 static CHANSERV_FUNC(cmd_allowregister
)
1879 const char *chan_name
= argv
[1];
1881 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1883 dict_remove(handle_dnrs
, chan_name
+1);
1884 reply("CSMSG_DNR_REMOVED", chan_name
);
1886 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1888 dict_remove(plain_dnrs
, chan_name
);
1889 reply("CSMSG_DNR_REMOVED", chan_name
);
1891 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1893 dict_remove(mask_dnrs
, chan_name
);
1894 reply("CSMSG_DNR_REMOVED", chan_name
);
1898 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1905 chanserv_get_owned_count(struct handle_info
*hi
)
1907 struct userData
*cList
;
1910 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1911 if(cList
->access
== UL_OWNER
)
1916 static CHANSERV_FUNC(cmd_register
)
1918 struct handle_info
*handle
;
1919 struct chanData
*cData
;
1920 struct modeNode
*mn
;
1921 char reason
[MAXLEN
];
1923 unsigned int new_channel
, force
=0;
1924 struct do_not_register
*dnr
;
1930 if(channel
->channel_info
)
1932 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1936 if(channel
->bad_channel
)
1938 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1942 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1944 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1949 chan_name
= channel
->name
;
1955 reply("MSG_MISSING_PARAMS", cmd
->name
);
1956 svccmd_send_help_brief(user
, chanserv
, cmd
);
1959 if(!IsChannelName(argv
[1]))
1961 reply("MSG_NOT_CHANNEL_NAME");
1965 if(opserv_bad_channel(argv
[1]))
1967 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1972 chan_name
= argv
[1];
1975 if(argc
>= (new_channel
+2))
1977 if(!IsHelping(user
))
1979 reply("CSMSG_PROXY_FORBIDDEN");
1983 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1985 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1986 dnr
= chanserv_is_dnr(chan_name
, handle
);
1988 /* Check if they are over the limit.. */
1989 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1991 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1998 handle
= user
->handle_info
;
1999 dnr
= chanserv_is_dnr(chan_name
, handle
);
2000 /* Check if they are over the limit.. */
2001 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2003 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2006 /* Check if another service is in the channel */
2008 for(n
= 0; n
< channel
->members
.used
; n
++)
2010 mn
= channel
->members
.list
[n
];
2011 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2013 reply("CSMSG_ANOTHER_SERVICE");
2020 if(!IsHelping(user
))
2021 reply("CSMSG_DNR_CHANNEL", chan_name
);
2023 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2027 /* now handled above for message specilization *
2028 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2030 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2036 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2038 cData
= register_channel(channel
, user
->handle_info
->handle
);
2039 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2040 cData
->modes
= chanserv_conf
.default_modes
;
2042 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2043 if (IsOffChannel(cData
))
2045 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2049 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2050 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2051 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2053 mod_chanmode_announce(chanserv
, channel
, change
);
2054 mod_chanmode_free(change
);
2057 /* Initialize the channel's max user record. */
2058 cData
->max
= channel
->members
.used
;
2060 if(handle
!= user
->handle_info
)
2061 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2063 reply("CSMSG_REG_SUCCESS", channel
->name
);
2065 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2066 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2071 make_confirmation_string(struct userData
*uData
)
2073 static char strbuf
[16];
2078 for(src
= uData
->handle
->handle
; *src
; )
2079 accum
= accum
* 31 + toupper(*src
++);
2081 for(src
= uData
->channel
->channel
->name
; *src
; )
2082 accum
= accum
* 31 + toupper(*src
++);
2083 sprintf(strbuf
, "%08x", accum
);
2087 static CHANSERV_FUNC(cmd_unregister
)
2090 char reason
[MAXLEN
];
2091 struct chanData
*cData
;
2092 struct userData
*uData
;
2094 cData
= channel
->channel_info
;
2097 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2101 uData
= GetChannelUser(cData
, user
->handle_info
);
2102 if(!uData
|| (uData
->access
< UL_OWNER
))
2104 reply("CSMSG_NO_ACCESS");
2108 if(IsProtected(cData
))
2110 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2114 if(!IsHelping(user
))
2116 const char *confirm_string
;
2117 if(IsSuspended(cData
))
2119 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2122 confirm_string
= make_confirmation_string(uData
);
2123 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2125 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2130 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2131 name
= strdup(channel
->name
);
2132 unregister_channel(cData
, reason
);
2133 reply("CSMSG_UNREG_SUCCESS", name
);
2138 static CHANSERV_FUNC(cmd_move
)
2140 struct mod_chanmode change
;
2141 struct chanNode
*target
;
2142 struct modeNode
*mn
;
2143 struct userData
*uData
;
2144 char reason
[MAXLEN
];
2145 struct do_not_register
*dnr
;
2149 if(IsProtected(channel
->channel_info
))
2151 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2155 if(!IsChannelName(argv
[1]))
2157 reply("MSG_NOT_CHANNEL_NAME");
2161 if(opserv_bad_channel(argv
[1]))
2163 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2167 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2169 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2171 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2173 if(!IsHelping(user
))
2174 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2176 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2182 mod_chanmode_init(&change
);
2183 if(!(target
= GetChannel(argv
[1])))
2185 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2186 if(!IsSuspended(channel
->channel_info
))
2187 AddChannelUser(chanserv
, target
);
2189 else if(target
->channel_info
)
2191 reply("CSMSG_ALREADY_REGGED", target
->name
);
2194 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2195 && !IsHelping(user
))
2197 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2200 else if(!IsSuspended(channel
->channel_info
))
2203 change
.args
[0].mode
= MODE_CHANOP
;
2204 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2205 mod_chanmode_announce(chanserv
, target
, &change
);
2210 /* Clear MODE_REGISTERED from old channel, add it to new. */
2212 change
.modes_clear
= MODE_REGISTERED
;
2213 mod_chanmode_announce(chanserv
, channel
, &change
);
2214 change
.modes_clear
= 0;
2215 change
.modes_set
= MODE_REGISTERED
;
2216 mod_chanmode_announce(chanserv
, target
, &change
);
2219 /* Move the channel_info to the target channel; it
2220 shouldn't be necessary to clear timeq callbacks
2221 for the old channel. */
2222 target
->channel_info
= channel
->channel_info
;
2223 target
->channel_info
->channel
= target
;
2224 channel
->channel_info
= NULL
;
2226 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2228 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2229 if(!IsSuspended(target
->channel_info
))
2231 char reason2
[MAXLEN
];
2232 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2233 DelChannelUser(chanserv
, channel
, reason2
, 0);
2235 UnlockChannel(channel
);
2236 LockChannel(target
);
2237 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2242 merge_users(struct chanData
*source
, struct chanData
*target
)
2244 struct userData
*suData
, *tuData
, *next
;
2250 /* Insert the source's users into the scratch area. */
2251 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2252 dict_insert(merge
, suData
->handle
->handle
, suData
);
2254 /* Iterate through the target's users, looking for
2255 users common to both channels. The lower access is
2256 removed from either the scratch area or target user
2258 for(tuData
= target
->users
; tuData
; tuData
= next
)
2260 struct userData
*choice
;
2262 next
= tuData
->next
;
2264 /* If a source user exists with the same handle as a target
2265 channel's user, resolve the conflict by removing one. */
2266 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2270 /* Pick the data we want to keep. */
2271 /* If the access is the same, use the later seen time. */
2272 if(suData
->access
== tuData
->access
)
2273 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2274 else /* Otherwise, keep the higher access level. */
2275 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2277 /* Remove the user that wasn't picked. */
2278 if(choice
== tuData
)
2280 dict_remove(merge
, suData
->handle
->handle
);
2281 del_channel_user(suData
, 0);
2284 del_channel_user(tuData
, 0);
2287 /* Move the remaining users to the target channel. */
2288 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2290 suData
= iter_data(it
);
2292 /* Insert the user into the target channel's linked list. */
2293 suData
->prev
= NULL
;
2294 suData
->next
= target
->users
;
2295 suData
->channel
= target
;
2298 target
->users
->prev
= suData
;
2299 target
->users
= suData
;
2301 /* Update the user counts for the target channel; the
2302 source counts are left alone. */
2303 target
->userCount
++;
2306 /* Possible to assert (source->users == NULL) here. */
2307 source
->users
= NULL
;
2312 merge_bans(struct chanData
*source
, struct chanData
*target
)
2314 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2316 /* Hold on to the original head of the target ban list
2317 to avoid comparing source bans with source bans. */
2318 tFront
= target
->bans
;
2320 /* Perform a totally expensive O(n*m) merge, ick. */
2321 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2323 /* Flag to track whether the ban's been moved
2324 to the destination yet. */
2327 /* Possible to assert (sbData->prev == NULL) here. */
2328 sNext
= sbData
->next
;
2330 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2332 tNext
= tbData
->next
;
2334 /* Perform two comparisons between each source
2335 and target ban, conflicts are resolved by
2336 keeping the broader ban and copying the later
2337 expiration and triggered time. */
2338 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2340 /* There is a broader ban in the target channel that
2341 overrides one in the source channel; remove the
2342 source ban and break. */
2343 if(sbData
->expires
> tbData
->expires
)
2344 tbData
->expires
= sbData
->expires
;
2345 if(sbData
->triggered
> tbData
->triggered
)
2346 tbData
->triggered
= sbData
->triggered
;
2347 del_channel_ban(sbData
);
2350 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2352 /* There is a broader ban in the source channel that
2353 overrides one in the target channel; remove the
2354 target ban, fall through and move the source over. */
2355 if(tbData
->expires
> sbData
->expires
)
2356 sbData
->expires
= tbData
->expires
;
2357 if(tbData
->triggered
> sbData
->triggered
)
2358 sbData
->triggered
= tbData
->triggered
;
2359 if(tbData
== tFront
)
2361 del_channel_ban(tbData
);
2364 /* Source bans can override multiple target bans, so
2365 we allow a source to run through this loop multiple
2366 times, but we can only move it once. */
2371 /* Remove the source ban from the source ban list. */
2373 sbData
->next
->prev
= sbData
->prev
;
2375 /* Modify the source ban's associated channel. */
2376 sbData
->channel
= target
;
2378 /* Insert the ban into the target channel's linked list. */
2379 sbData
->prev
= NULL
;
2380 sbData
->next
= target
->bans
;
2383 target
->bans
->prev
= sbData
;
2384 target
->bans
= sbData
;
2386 /* Update the user counts for the target channel. */
2391 /* Possible to assert (source->bans == NULL) here. */
2392 source
->bans
= NULL
;
2396 merge_data(struct chanData
*source
, struct chanData
*target
)
2398 if(source
->visited
> target
->visited
)
2399 target
->visited
= source
->visited
;
2403 merge_channel(struct chanData
*source
, struct chanData
*target
)
2405 merge_users(source
, target
);
2406 merge_bans(source
, target
);
2407 merge_data(source
, target
);
2410 static CHANSERV_FUNC(cmd_merge
)
2412 struct userData
*target_user
;
2413 struct chanNode
*target
;
2414 char reason
[MAXLEN
];
2418 /* Make sure the target channel exists and is registered to the user
2419 performing the command. */
2420 if(!(target
= GetChannel(argv
[1])))
2422 reply("MSG_INVALID_CHANNEL");
2426 if(!target
->channel_info
)
2428 reply("CSMSG_NOT_REGISTERED", target
->name
);
2432 if(IsProtected(channel
->channel_info
))
2434 reply("CSMSG_MERGE_NODELETE");
2438 if(IsSuspended(target
->channel_info
))
2440 reply("CSMSG_MERGE_SUSPENDED");
2444 if(channel
== target
)
2446 reply("CSMSG_MERGE_SELF");
2450 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2451 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2453 reply("CSMSG_MERGE_NOT_OWNER");
2457 /* Merge the channel structures and associated data. */
2458 merge_channel(channel
->channel_info
, target
->channel_info
);
2459 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2460 unregister_channel(channel
->channel_info
, reason
);
2461 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2465 static CHANSERV_FUNC(cmd_opchan
)
2467 struct mod_chanmode change
;
2468 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2470 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2473 channel
->channel_info
->may_opchan
= 0;
2474 mod_chanmode_init(&change
);
2476 change
.args
[0].mode
= MODE_CHANOP
;
2477 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2478 mod_chanmode_announce(chanserv
, channel
, &change
);
2479 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2483 static CHANSERV_FUNC(cmd_adduser
)
2485 struct userData
*actee
;
2486 struct userData
*actor
;
2487 struct handle_info
*handle
;
2488 unsigned short access
;
2492 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2494 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2498 access
= user_level_from_name(argv
[2], UL_OWNER
);
2501 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2505 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2506 if(actor
->access
<= access
)
2508 reply("CSMSG_NO_BUMP_ACCESS");
2512 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2514 // 'kevin must first authenticate with AuthServ.' is sent to user
2515 struct userNode
*unode
;
2516 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2519 if(find_adduser_pending(channel
, unode
)) {
2520 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2523 if(IsInChannel(channel
, unode
)) {
2524 reply("CSMSG_ADDUSER_PENDING");
2525 add_adduser_pending(channel
, unode
, access
);
2526 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2528 /* this results in user must auth AND not in chan errors. too confusing..
2530 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2538 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2540 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2544 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2545 scan_user_presence(actee
, NULL
);
2546 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2550 static CHANSERV_FUNC(cmd_clvl
)
2552 struct handle_info
*handle
;
2553 struct userData
*victim
;
2554 struct userData
*actor
;
2555 unsigned short new_access
;
2556 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2560 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2562 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2565 if(handle
== user
->handle_info
&& !privileged
)
2567 reply("CSMSG_NO_SELF_CLVL");
2571 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2573 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2577 if(actor
->access
<= victim
->access
&& !privileged
)
2579 reply("MSG_USER_OUTRANKED", handle
->handle
);
2583 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2587 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2591 if(new_access
>= actor
->access
&& !privileged
)
2593 reply("CSMSG_NO_BUMP_ACCESS");
2597 victim
->access
= new_access
;
2598 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2602 static CHANSERV_FUNC(cmd_deluser
)
2604 struct handle_info
*handle
;
2605 struct userData
*victim
;
2606 struct userData
*actor
;
2607 unsigned short access
;
2612 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2614 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2617 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2619 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2625 access
= user_level_from_name(argv
[1], UL_OWNER
);
2628 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2631 if(access
!= victim
->access
)
2633 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2639 access
= victim
->access
;
2642 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2644 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2648 chan_name
= strdup(channel
->name
);
2649 del_channel_user(victim
, 1);
2650 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2656 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2658 struct userData
*actor
, *uData
, *next
;
2660 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2662 if(min_access
> max_access
)
2664 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2668 if((actor
->access
<= max_access
) && !IsHelping(user
))
2670 reply("CSMSG_NO_ACCESS");
2674 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2678 if((uData
->access
>= min_access
)
2679 && (uData
->access
<= max_access
)
2680 && match_ircglob(uData
->handle
->handle
, mask
))
2681 del_channel_user(uData
, 1);
2684 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2688 static CHANSERV_FUNC(cmd_mdelowner
)
2690 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2693 static CHANSERV_FUNC(cmd_mdelcoowner
)
2695 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2698 static CHANSERV_FUNC(cmd_mdelmanager
)
2700 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2703 static CHANSERV_FUNC(cmd_mdelop
)
2705 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2708 static CHANSERV_FUNC(cmd_mdelpeon
)
2710 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2713 static CHANSERV_FUNC(cmd_mdelhalfop
)
2715 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2721 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2723 struct banData
*bData
, *next
;
2724 char interval
[INTERVALLEN
];
2729 limit
= now
- duration
;
2730 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2734 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2737 del_channel_ban(bData
);
2741 intervalString(interval
, duration
, user
->handle_info
);
2742 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2747 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2749 struct userData
*actor
, *uData
, *next
;
2750 char interval
[INTERVALLEN
];
2754 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2755 if(min_access
> max_access
)
2757 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2761 if((actor
->access
<= max_access
) && !IsHelping(user
))
2763 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2768 limit
= now
- duration
;
2769 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2773 if((uData
->seen
> limit
) || uData
->present
)
2776 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2777 || (!max_access
&& (uData
->access
< actor
->access
)))
2779 del_channel_user(uData
, 1);
2787 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2789 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2793 static CHANSERV_FUNC(cmd_trim
)
2795 unsigned long duration
;
2796 unsigned short min_level
, max_level
;
2800 duration
= ParseInterval(argv
[2]);
2803 reply("CSMSG_CANNOT_TRIM");
2807 if(!irccasecmp(argv
[1], "lamers"))
2809 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2812 else if(!irccasecmp(argv
[1], "users"))
2814 cmd_trim_users(user
, channel
, 0, 0, duration
);
2817 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2819 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2822 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2824 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2829 reply("CSMSG_INVALID_TRIM", argv
[1]);
2834 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2835 to the user. cmd_all takes advantage of this. */
2836 static CHANSERV_FUNC(cmd_up
)
2838 struct mod_chanmode change
;
2839 struct userData
*uData
;
2842 mod_chanmode_init(&change
);
2844 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2845 if(!change
.args
[0].u
.member
)
2848 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2852 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2856 reply("CSMSG_GODMODE_UP", argv
[0]);
2859 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2861 change
.args
[0].mode
= MODE_CHANOP
;
2862 errmsg
= "CSMSG_ALREADY_OPPED";
2864 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2866 change
.args
[0].mode
= MODE_HALFOP
;
2867 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2869 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chVoice
] == 'p' || channel
->channel_info
->chOpts
[chVoice
] == 'a'))
2871 change
.args
[0].mode
= MODE_VOICE
;
2872 errmsg
= "CSMSG_ALREADY_VOICED";
2877 reply("CSMSG_NO_ACCESS");
2880 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2881 if(!change
.args
[0].mode
)
2884 reply(errmsg
, channel
->name
);
2887 modcmd_chanmode_announce(&change
);
2891 static CHANSERV_FUNC(cmd_down
)
2893 struct mod_chanmode change
;
2895 mod_chanmode_init(&change
);
2897 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2898 if(!change
.args
[0].u
.member
)
2901 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2905 if(!change
.args
[0].u
.member
->modes
)
2908 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2912 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2913 modcmd_chanmode_announce(&change
);
2917 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
)
2919 struct userData
*cList
;
2921 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2923 if(IsSuspended(cList
->channel
)
2924 || IsUserSuspended(cList
)
2925 || !GetUserMode(cList
->channel
->channel
, user
))
2928 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2934 static CHANSERV_FUNC(cmd_upall
)
2936 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2939 static CHANSERV_FUNC(cmd_downall
)
2941 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2944 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2945 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2948 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
)
2950 unsigned int ii
, valid
;
2951 struct userNode
*victim
;
2952 struct mod_chanmode
*change
;
2954 change
= mod_chanmode_alloc(argc
- 1);
2956 for(ii
=valid
=0; ++ii
< argc
; )
2958 if(!(victim
= GetUserH(argv
[ii
])))
2960 change
->args
[valid
].mode
= mode
;
2961 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2962 if(!change
->args
[valid
].u
.member
)
2964 if(validate
&& !validate(user
, channel
, victim
))
2969 change
->argc
= valid
;
2970 if(valid
< (argc
-1))
2971 reply("CSMSG_PROCESS_FAILED");
2974 modcmd_chanmode_announce(change
);
2975 reply(action
, channel
->name
);
2977 mod_chanmode_free(change
);
2981 static CHANSERV_FUNC(cmd_op
)
2983 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2986 static CHANSERV_FUNC(cmd_hop
)
2988 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2991 static CHANSERV_FUNC(cmd_deop
)
2993 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2996 static CHANSERV_FUNC(cmd_dehop
)
2998 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3001 static CHANSERV_FUNC(cmd_voice
)
3003 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3006 static CHANSERV_FUNC(cmd_devoice
)
3008 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3012 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
3018 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3020 struct modeNode
*mn
= channel
->members
.list
[ii
];
3022 if(IsService(mn
->user
))
3025 if(!user_matches_glob(mn
->user
, ban
, 1))
3028 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3032 victims
[(*victimCount
)++] = mn
;
3038 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3040 struct userNode
*victim
;
3041 struct modeNode
**victims
;
3042 unsigned int offset
, n
, victimCount
, duration
= 0;
3043 char *reason
= "Bye.", *ban
, *name
;
3044 char interval
[INTERVALLEN
];
3046 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3047 REQUIRE_PARAMS(offset
);
3050 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3051 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3053 /* Truncate the reason to a length of TOPICLEN, as
3054 the ircd does; however, leave room for an ellipsis
3055 and the kicker's nick. */
3056 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3060 if((victim
= GetUserH(argv
[1])))
3062 victims
= alloca(sizeof(victims
[0]));
3063 victims
[0] = GetUserMode(channel
, victim
);
3064 /* XXX: The comparison with ACTION_KICK is just because all
3065 * other actions can work on users outside the channel, and we
3066 * want to allow those (e.g. unbans) in that case. If we add
3067 * some other ejection action for in-channel users, change
3069 victimCount
= victims
[0] ? 1 : 0;
3071 if(IsService(victim
))
3074 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3078 if((action
== ACTION_KICK
) && !victimCount
)
3081 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3085 if(protect_user(victim
, user
, channel
->channel_info
))
3087 // This translates to send_message(user, cmd->parent->bot, ...)
3088 // if user is x3 (ctcp action) cmd is null and segfault.
3090 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3094 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3095 name
= victim
->nick
;
3099 if(!is_ircmask(argv
[1]))
3102 reply("MSG_NICK_UNKNOWN", argv
[1]);
3106 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3108 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3111 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3114 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3116 victimCount
= 0; /* Dont deop etc ppl who match this */
3118 #ifdef entropy_lameness
3119 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
3122 reply("CSMSG_LAME_MASK", argv
[1]);
3127 if((action
== ACTION_KICK
) && (victimCount
== 0))
3130 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3134 name
= ban
= strdup(argv
[1]);
3137 /* Truncate the ban in place if necessary; we must ensure
3138 that 'ban' is a valid ban mask before sanitizing it. */
3139 sanitize_ircmask(ban
);
3141 if(action
& ACTION_ADD_LAMER
)
3143 struct banData
*bData
, *next
;
3145 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3148 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3153 if(action
& ACTION_ADD_TIMED_LAMER
)
3155 duration
= ParseInterval(argv
[2]);
3160 reply("CSMSG_DURATION_TOO_LOW");
3164 else if(duration
> (86400 * 365 * 2))
3167 reply("CSMSG_DURATION_TOO_HIGH");
3174 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3176 if(match_ircglobs(bData
->mask
, ban
))
3178 int exact
= !irccasecmp(bData
->mask
, ban
);
3180 /* The ban is redundant; there is already a ban
3181 with the same effect in place. */
3185 free(bData
->reason
);
3186 bData
->reason
= strdup(reason
);
3187 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3189 reply("CSMSG_REASON_CHANGE", ban
);
3193 if(exact
&& bData
->expires
)
3197 /* If the ban matches an existing one exactly,
3198 extend the expiration time if the provided
3199 duration is longer. */
3200 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3202 bData
->expires
= now
+ duration
;
3213 /* Delete the expiration timeq entry and
3214 requeue if necessary. */
3215 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3218 timeq_add(bData
->expires
, expire_ban
, bData
);
3222 /* automated kickban, dont reply */
3225 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3227 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3233 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3240 if(match_ircglobs(ban
, bData
->mask
))
3242 /* The ban we are adding makes previously existing
3243 bans redundant; silently remove them. */
3244 del_channel_ban(bData
);
3248 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
);
3250 name
= ban
= strdup(bData
->mask
);
3254 /* WHAT DOES THIS DO?? -Rubin */
3255 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3257 extern const char *hidden_host_suffix
;
3258 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3260 unsigned int l1
, l2
;
3263 l2
= strlen(old_name
);
3266 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3268 new_mask
= malloc(MAXLEN
);
3269 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3271 name
= ban
= new_mask
;
3276 if(action
& ACTION_BAN
)
3278 unsigned int exists
;
3279 struct mod_chanmode
*change
;
3281 if(channel
->banlist
.used
>= MAXBANS
)
3284 reply("CSMSG_BANLIST_FULL", channel
->name
);
3289 exists
= ChannelBanExists(channel
, ban
);
3290 change
= mod_chanmode_alloc(victimCount
+ 1);
3291 for(n
= 0; n
< victimCount
; ++n
)
3293 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3294 change
->args
[n
].u
.member
= victims
[n
];
3298 change
->args
[n
].mode
= MODE_BAN
;
3299 change
->args
[n
++].u
.hostmask
= ban
;
3303 modcmd_chanmode_announce(change
);
3305 mod_chanmode_announce(chanserv
, channel
, change
);
3306 mod_chanmode_free(change
);
3308 if(exists
&& (action
== ACTION_BAN
))
3311 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3317 if(action
& ACTION_KICK
)
3319 char kick_reason
[MAXLEN
];
3320 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3322 for(n
= 0; n
< victimCount
; n
++)
3323 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3328 /* No response, since it was automated. */
3330 else if(action
& ACTION_ADD_LAMER
)
3333 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3335 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3337 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3338 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3339 else if(action
& ACTION_BAN
)
3340 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3341 else if(action
& ACTION_KICK
&& victimCount
)
3342 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3348 static CHANSERV_FUNC(cmd_kickban
)
3350 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3353 static CHANSERV_FUNC(cmd_kick
)
3355 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3358 static CHANSERV_FUNC(cmd_ban
)
3360 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3363 static CHANSERV_FUNC(cmd_addlamer
)
3365 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3368 static CHANSERV_FUNC(cmd_addtimedlamer
)
3370 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3373 static struct mod_chanmode
*
3374 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3376 struct mod_chanmode
*change
;
3377 unsigned char *match
;
3378 unsigned int ii
, count
;
3380 match
= alloca(bans
->used
);
3383 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3385 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3392 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3394 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3401 change
= mod_chanmode_alloc(count
);
3402 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3406 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3407 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3409 assert(count
== change
->argc
);
3413 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3415 unsigned int jj
, ii
, count
;
3417 struct chanData
*channel
;
3419 struct mod_chanmode
*change
;
3421 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3422 /* Walk through every channel */
3423 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3424 switch(channel
->chOpts
[chBanTimeout
])
3426 default: case '0': continue; /* Dont remove bans in this chan */
3427 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3428 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3429 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3430 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3431 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3434 /* First find out how many bans were going to unset */
3435 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3436 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3440 /* At least one ban, so setup a removal */
3441 change
= mod_chanmode_alloc(count
);
3443 /* Walk over every ban in this channel.. */
3444 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3445 bn
= channel
->channel
->banlist
.list
[jj
];
3446 if (bn
->set
< bantimeout
) {
3447 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3449 /* Add this ban to the mode change */
3450 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3451 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3453 /* Pull this ban out of the list */
3454 banList_remove(&(channel
->channel
->banlist
), bn
);
3459 /* Send the modes to IRC */
3460 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3462 /* free memory from strdup above */
3463 for(ii
= 0; ii
< count
; ++ii
)
3464 free((char*)change
->args
[ii
].u
.hostmask
);
3466 mod_chanmode_free(change
);
3469 /* Set this function to run again */
3470 if(chanserv_conf
.ban_timeout_frequency
)
3471 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3476 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3478 struct userNode
*actee
;
3484 /* may want to allow a comma delimited list of users... */
3485 if(!(actee
= GetUserH(argv
[1])))
3487 if(!is_ircmask(argv
[1]))
3489 reply("MSG_NICK_UNKNOWN", argv
[1]);
3493 mask
= strdup(argv
[1]);
3496 /* We don't sanitize the mask here because ircu
3498 if(action
& ACTION_UNBAN
)
3500 struct mod_chanmode
*change
;
3501 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3506 modcmd_chanmode_announce(change
);
3507 for(ii
= 0; ii
< change
->argc
; ++ii
)
3508 free((char*)change
->args
[ii
].u
.hostmask
);
3509 mod_chanmode_free(change
);
3514 if(action
& ACTION_DEL_LAMER
)
3516 struct banData
*ban
, *next
;
3518 ban
= channel
->channel_info
->bans
; /* lamers */
3522 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3525 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3530 del_channel_ban(ban
);
3537 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3539 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3545 static CHANSERV_FUNC(cmd_unban
)
3547 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3550 static CHANSERV_FUNC(cmd_dellamer
)
3552 /* it doesn't necessarily have to remove the channel ban - may want
3553 to make that an option. */
3554 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3557 static CHANSERV_FUNC(cmd_unbanme
)
3559 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3560 long flags
= ACTION_UNBAN
;
3562 /* remove permanent bans if the user has the proper access. */
3563 if(uData
->access
>= UL_MANAGER
)
3564 flags
|= ACTION_DEL_LAMER
;
3566 argv
[1] = user
->nick
;
3567 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3570 static CHANSERV_FUNC(cmd_unbanall
)
3572 struct mod_chanmode
*change
;
3575 if(!channel
->banlist
.used
)
3577 reply("CSMSG_NO_BANS", channel
->name
);
3581 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3582 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3584 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3585 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3587 modcmd_chanmode_announce(change
);
3588 for(ii
= 0; ii
< change
->argc
; ++ii
)
3589 free((char*)change
->args
[ii
].u
.hostmask
);
3590 mod_chanmode_free(change
);
3591 reply("CSMSG_BANS_REMOVED", channel
->name
);
3595 static CHANSERV_FUNC(cmd_open
)
3597 struct mod_chanmode
*change
;
3600 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3602 change
= mod_chanmode_alloc(0);
3603 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3604 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3605 && channel
->channel_info
->modes
.modes_set
)
3606 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3607 modcmd_chanmode_announce(change
);
3608 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3609 for(ii
= 0; ii
< change
->argc
; ++ii
)
3610 free((char*)change
->args
[ii
].u
.hostmask
);
3611 mod_chanmode_free(change
);
3615 static CHANSERV_FUNC(cmd_myaccess
)
3617 static struct string_buffer sbuf
;
3618 struct handle_info
*target_handle
;
3619 struct userData
*uData
;
3622 target_handle
= user
->handle_info
;
3623 else if(!IsHelping(user
))
3625 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3628 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3631 if(!target_handle
->channels
)
3633 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3637 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3638 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3640 struct chanData
*cData
= uData
->channel
;
3642 if(uData
->access
> UL_OWNER
)
3644 if(IsProtected(cData
)
3645 && (target_handle
!= user
->handle_info
)
3646 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3649 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3650 if(uData
->flags
== USER_AUTO_OP
)
3651 string_buffer_append(&sbuf
, ',');
3652 if(IsUserSuspended(uData
))
3653 string_buffer_append(&sbuf
, 's');
3654 if(IsUserAutoOp(uData
))
3656 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3657 string_buffer_append(&sbuf
, 'o');
3658 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3659 string_buffer_append(&sbuf
, 'h');
3660 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3661 string_buffer_append(&sbuf
, 'v');
3663 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3664 string_buffer_append(&sbuf
, 'i');
3666 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3668 string_buffer_append_string(&sbuf
, ")]");
3669 string_buffer_append(&sbuf
, '\0');
3670 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3676 static CHANSERV_FUNC(cmd_access
)
3678 struct userNode
*target
;
3679 struct handle_info
*target_handle
;
3680 struct userData
*uData
;
3682 char prefix
[MAXLEN
];
3687 target_handle
= target
->handle_info
;
3689 else if((target
= GetUserH(argv
[1])))
3691 target_handle
= target
->handle_info
;
3693 else if(argv
[1][0] == '*')
3695 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3697 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3703 reply("MSG_NICK_UNKNOWN", argv
[1]);
3707 assert(target
|| target_handle
);
3709 if(target
== chanserv
)
3711 reply("CSMSG_IS_CHANSERV");
3719 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3724 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3727 reply("MSG_AUTHENTICATE");
3733 const char *epithet
= NULL
, *type
= NULL
;
3736 epithet
= chanserv_conf
.irc_operator_epithet
;
3739 else if(IsNetworkHelper(target
))
3741 epithet
= chanserv_conf
.network_helper_epithet
;
3742 type
= "network helper";
3744 else if(IsSupportHelper(target
))
3746 epithet
= chanserv_conf
.support_helper_epithet
;
3747 type
= "support helper";
3751 if(target_handle
->epithet
)
3752 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3754 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3756 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3760 sprintf(prefix
, "%s", target_handle
->handle
);
3763 if(!channel
->channel_info
)
3765 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3769 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3770 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3771 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3773 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3774 /* To prevent possible information leaks, only show infolines
3775 * if the requestor is in the channel or it's their own
3777 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3779 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3781 /* Likewise, only say it's suspended if the user has active
3782 * access in that channel or it's their own entry. */
3783 if(IsUserSuspended(uData
)
3784 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3785 || (user
->handle_info
== uData
->handle
)))
3787 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3792 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3798 /* This is never used...
3800 zoot_list(struct listData *list)
3802 struct userData *uData;
3803 unsigned int start, curr, highest, lowest;
3804 struct helpfile_table tmp_table;
3805 const char **temp, *msg;
3807 if(list->table.length == 1)
3810 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);
3812 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));
3813 msg = user_find_message(list->user, "MSG_NONE");
3814 send_message_type(4, list->user, list->bot, " %s", msg);
3816 tmp_table.width = list->table.width;
3817 tmp_table.flags = list->table.flags;
3818 list->table.contents[0][0] = " ";
3819 highest = list->highest;
3820 if(list->lowest != 0)
3821 lowest = list->lowest;
3822 else if(highest < 100)
3825 lowest = highest - 100;
3826 for(start = curr = 1; curr < list->table.length; )
3828 uData = list->users[curr-1];
3829 list->table.contents[curr++][0] = " ";
3830 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3833 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);
3835 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));
3836 temp = list->table.contents[--start];
3837 list->table.contents[start] = list->table.contents[0];
3838 tmp_table.contents = list->table.contents + start;
3839 tmp_table.length = curr - start;
3840 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3841 list->table.contents[start] = temp;
3843 highest = lowest - 1;
3844 lowest = (highest < 100) ? 0 : (highest - 99);
3851 def_list(struct listData
*list
)
3855 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
);
3857 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
));
3858 if(list
->table
.length
== 1)
3860 msg
= user_find_message(list
->user
, "MSG_NONE");
3861 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3864 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3868 userData_access_comp(const void *arg_a
, const void *arg_b
)
3870 const struct userData
*a
= *(struct userData
**)arg_a
;
3871 const struct userData
*b
= *(struct userData
**)arg_b
;
3873 if(a
->access
!= b
->access
)
3874 res
= b
->access
- a
->access
;
3876 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3881 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3883 void (*send_list
)(struct listData
*);
3884 struct userData
*uData
;
3885 struct listData lData
;
3886 unsigned int matches
;
3890 lData
.bot
= cmd
->parent
->bot
;
3891 lData
.channel
= channel
;
3892 lData
.lowest
= lowest
;
3893 lData
.highest
= highest
;
3894 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3895 send_list
= def_list
;
3896 /* What does the following line do exactly?? */
3897 /*(void)zoot_list; ** since it doesn't show user levels */
3899 /* this does nothing!! -rubin
3900 if(user->handle_info)
3902 switch(user->handle_info->userlist_style)
3904 case HI_STYLE_DEF: send_list = def_list; break;
3905 case HI_STYLE_ZOOT: send_list = def_list; break;
3910 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3912 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3914 if((uData
->access
< lowest
)
3915 || (uData
->access
> highest
)
3916 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3918 lData
.users
[matches
++] = uData
;
3920 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3922 lData
.table
.length
= matches
+1;
3923 lData
.table
.width
= 5;
3924 lData
.table
.flags
= TABLE_NO_FREE
;
3925 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3926 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3927 lData
.table
.contents
[0] = ary
;
3931 ary
[3] = "Last Seen";
3933 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3935 struct userData
*uData
= lData
.users
[matches
-1];
3936 char seen
[INTERVALLEN
];
3938 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3939 lData
.table
.contents
[matches
] = ary
;
3940 /* ary[0] = strtab(uData->access);*/
3941 ary
[0] = user_level_name_from_level(uData
->access
);
3942 ary
[1] = strtab(uData
->access
);
3943 ary
[2] = uData
->handle
->handle
;
3946 else if(!uData
->seen
)
3949 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3950 ary
[3] = strdup(ary
[3]);
3951 if(IsUserSuspended(uData
))
3952 ary
[4] = "Suspended";
3953 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3954 ary
[4] = "Vacation";
3959 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3961 free((char*)lData
.table
.contents
[matches
][3]);
3962 free(lData
.table
.contents
[matches
]);
3964 free(lData
.table
.contents
[0]);
3965 free(lData
.table
.contents
);
3969 /* Remove this now that debugging is over? or improve it for
3970 * users? Would it be better tied into USERS somehow? -Rubin */
3971 static CHANSERV_FUNC(cmd_pending
)
3973 struct adduserPending
*ap
;
3974 reply("CSMSG_ADDUSER_PENDING_HEADER");
3976 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3977 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3978 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3982 static CHANSERV_FUNC(cmd_users
)
3984 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3987 static CHANSERV_FUNC(cmd_wlist
)
3989 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3992 static CHANSERV_FUNC(cmd_clist
)
3994 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3997 static CHANSERV_FUNC(cmd_mlist
)
3999 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4002 static CHANSERV_FUNC(cmd_olist
)
4004 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4007 static CHANSERV_FUNC(cmd_hlist
)
4009 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4012 static CHANSERV_FUNC(cmd_plist
)
4014 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4017 static CHANSERV_FUNC(cmd_lamers
)
4019 struct helpfile_table tbl
;
4020 unsigned int matches
= 0, timed
= 0, ii
;
4021 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4022 const char *msg_never
, *triggered
, *expires
;
4023 struct banData
*ban
, **bans
; /* lamers */
4030 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4031 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4034 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4036 if(search
&& !match_ircglobs(search
, ban
->mask
))
4038 bans
[matches
++] = ban
;
4043 tbl
.length
= matches
+ 1;
4044 tbl
.width
= 4 + timed
;
4046 tbl
.flags
= TABLE_NO_FREE
;
4047 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4048 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4049 tbl
.contents
[0][0] = "Mask";
4050 tbl
.contents
[0][1] = "Set By";
4051 tbl
.contents
[0][2] = "Triggered";
4054 tbl
.contents
[0][3] = "Expires";
4055 tbl
.contents
[0][4] = "Reason";
4058 tbl
.contents
[0][3] = "Reason";
4061 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4063 free(tbl
.contents
[0]);
4068 msg_never
= user_find_message(user
, "MSG_NEVER");
4069 for(ii
= 0; ii
< matches
; )
4075 else if(ban
->expires
)
4076 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4078 expires
= msg_never
;
4081 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4083 triggered
= msg_never
;
4085 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4086 tbl
.contents
[ii
][0] = ban
->mask
;
4087 tbl
.contents
[ii
][1] = ban
->owner
;
4088 tbl
.contents
[ii
][2] = strdup(triggered
);
4091 tbl
.contents
[ii
][3] = strdup(expires
);
4092 tbl
.contents
[ii
][4] = ban
->reason
;
4095 tbl
.contents
[ii
][3] = ban
->reason
;
4097 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4098 /* reply("MSG_MATCH_COUNT", matches); */
4099 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4101 free((char*)tbl
.contents
[ii
][2]);
4103 free((char*)tbl
.contents
[ii
][3]);
4104 free(tbl
.contents
[ii
]);
4106 free(tbl
.contents
[0]);
4113 * return + if the user does NOT have the right to set the topic, and
4114 * the topic is changed.
4117 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4119 struct chanData
*cData
= channel
->channel_info
;
4120 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4122 else if(cData
->topic
)
4123 return irccasecmp(new_topic
, cData
->topic
);
4130 * Makes a givin topic fit into a givin topic mask and returns
4133 * topic_mask - the mask to conform to
4134 * topic - the topic to make conform
4135 * new_topic - the pre-allocated char* to put the new topic into
4137 * modifies: new_topic
4140 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4142 //char *topic_mask = cData->topic_mask;
4144 int pos
=0, starpos
=-1, dpos
=0, len
;
4146 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4153 strcpy(new_topic
, "");
4156 len
= strlen(topic
);
4157 if((dpos
+ len
) > TOPICLEN
)
4158 len
= TOPICLEN
+ 1 - dpos
;
4159 memcpy(new_topic
+dpos
, topic
, len
);
4163 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4164 default: new_topic
[dpos
++] = tchar
; break;
4167 if((dpos
> TOPICLEN
) || tchar
)
4169 strcpy(new_topic
, "");
4172 new_topic
[dpos
] = 0;
4176 static CHANSERV_FUNC(cmd_topic
)
4178 struct chanData
*cData
;
4181 cData
= channel
->channel_info
;
4186 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4187 reply("CSMSG_TOPIC_SET", cData
->topic
);
4191 reply("CSMSG_NO_TOPIC", channel
->name
);
4195 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4196 /* If they say "!topic *", use an empty topic. */
4197 if((topic
[0] == '*') && (topic
[1] == 0))
4200 if(bad_topic(channel
, user
, topic
))
4202 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4207 /* If there is a topicmask set, and the new topic doesnt match, make it */
4208 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4210 char *topic_mask
= cData
->topic_mask
;
4211 char new_topic
[TOPICLEN
+1];
4213 /* make a new topic fitting mask */
4214 conform_topic(topic_mask
, topic
, new_topic
);
4217 /* Topic couldnt fit into mask, was too long */
4218 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4219 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4222 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4224 else /* No mask set, just set the topic */
4225 SetChannelTopic(channel
, chanserv
, topic
, 1);
4228 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4230 /* Grab the topic and save it as the default topic. */
4232 cData
->topic
= strdup(channel
->topic
);
4238 static CHANSERV_FUNC(cmd_mode
)
4240 struct mod_chanmode
*change
;
4244 change
= &channel
->channel_info
->modes
;
4245 if(change
->modes_set
|| change
->modes_clear
) {
4246 modcmd_chanmode_announce(change
);
4247 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4249 reply("CSMSG_NO_MODES", channel
->name
);
4253 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4256 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4260 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4261 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4264 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4265 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4269 modcmd_chanmode_announce(change
);
4270 mod_chanmode_free(change
);
4271 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4275 static CHANSERV_FUNC(cmd_invite
)
4277 struct userData
*uData
;
4278 struct userNode
*invite
;
4280 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4284 if(!(invite
= GetUserH(argv
[1])))
4286 reply("MSG_NICK_UNKNOWN", argv
[1]);
4293 if(GetUserMode(channel
, invite
))
4295 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4303 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4304 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4307 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4309 irc_invite(chanserv
, invite
, channel
);
4311 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4316 static CHANSERV_FUNC(cmd_inviteme
)
4318 if(GetUserMode(channel
, user
))
4320 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4323 if(channel
->channel_info
4324 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4326 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4329 irc_invite(cmd
->parent
->bot
, user
, channel
);
4334 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4337 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4339 /* We display things based on two dimensions:
4340 * - Issue time: present or absent
4341 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4342 * (in order of precedence, so something both expired and revoked
4343 * only counts as revoked)
4345 combo
= (suspended
->issued
? 4 : 0)
4346 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4348 case 0: /* no issue time, indefinite expiration */
4349 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4351 case 1: /* no issue time, expires in future */
4352 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4353 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4355 case 2: /* no issue time, expired */
4356 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4357 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4359 case 3: /* no issue time, revoked */
4360 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4361 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4363 case 4: /* issue time set, indefinite expiration */
4364 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4365 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4367 case 5: /* issue time set, expires in future */
4368 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4369 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4370 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4372 case 6: /* issue time set, expired */
4373 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4374 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4375 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4377 case 7: /* issue time set, revoked */
4378 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4379 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4380 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4383 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4388 static CHANSERV_FUNC(cmd_info
)
4390 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4391 struct userData
*uData
, *owner
;
4392 struct chanData
*cData
;
4393 struct do_not_register
*dnr
;
4398 cData
= channel
->channel_info
;
4399 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4402 uData
= GetChannelUser(cData
, user
->handle_info
);
4403 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4405 mod_chanmode_format(&cData
->modes
, modes
);
4406 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4407 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4410 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4414 note
= iter_data(it
);
4415 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4418 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4419 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4422 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4423 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4424 if(owner
->access
== UL_OWNER
)
4425 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4426 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4427 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4428 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4429 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4431 privileged
= IsStaff(user
);
4432 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4433 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4435 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4436 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4438 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4440 struct suspended
*suspended
;
4441 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4442 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4443 show_suspension_info(cmd
, user
, suspended
);
4445 else if(IsSuspended(cData
))
4447 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4448 show_suspension_info(cmd
, user
, cData
->suspended
);
4450 reply("CSMSG_CHANNEL_END");
4454 static CHANSERV_FUNC(cmd_netinfo
)
4456 extern time_t boot_time
;
4457 extern unsigned long burst_length
;
4458 char interval
[INTERVALLEN
];
4460 reply("CSMSG_NETWORK_INFO");
4461 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4462 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4463 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4464 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4465 reply("CSMSG_NETWORK_LAMERS", banCount
);
4466 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4467 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4468 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4473 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4475 struct helpfile_table table
;
4477 struct userNode
*user
;
4482 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4483 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4484 for(nn
=0; nn
<list
->used
; nn
++)
4486 user
= list
->list
[nn
];
4487 if(user
->modes
& skip_flags
)
4491 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4494 nick
= alloca(strlen(user
->nick
)+3);
4495 sprintf(nick
, "(%s)", user
->nick
);
4499 table
.contents
[table
.length
][0] = nick
;
4502 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4505 static CHANSERV_FUNC(cmd_ircops
)
4507 reply("CSMSG_STAFF_OPERS");
4508 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4512 static CHANSERV_FUNC(cmd_helpers
)
4514 reply("CSMSG_STAFF_HELPERS");
4515 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4519 static CHANSERV_FUNC(cmd_staff
)
4521 reply("CSMSG_NETWORK_STAFF");
4522 cmd_ircops(CSFUNC_ARGS
);
4523 cmd_helpers(CSFUNC_ARGS
);
4527 static CHANSERV_FUNC(cmd_peek
)
4529 struct modeNode
*mn
;
4530 char modes
[MODELEN
];
4532 struct helpfile_table table
;
4534 irc_make_chanmode(channel
, modes
);
4536 reply("CSMSG_PEEK_INFO", channel
->name
);
4538 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4539 reply("CSMSG_PEEK_MODES", modes
);
4540 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4544 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4545 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4546 for(n
= 0; n
< channel
->members
.used
; n
++)
4548 mn
= channel
->members
.list
[n
];
4549 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4551 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4552 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4557 reply("CSMSG_PEEK_OPS");
4558 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4561 reply("CSMSG_PEEK_NO_OPS");
4562 reply("CSMSG_PEEK_END");
4566 static MODCMD_FUNC(cmd_wipeinfo
)
4568 struct handle_info
*victim
;
4569 struct userData
*ud
, *actor
;
4572 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4573 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4575 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4577 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4580 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4582 reply("MSG_USER_OUTRANKED", victim
->handle
);
4588 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4592 static CHANSERV_FUNC(cmd_resync
)
4594 struct mod_chanmode
*changes
;
4595 struct chanData
*cData
= channel
->channel_info
;
4596 unsigned int ii
, used
;
4598 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4599 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4601 struct modeNode
*mn
= channel
->members
.list
[ii
];
4602 struct userData
*uData
;
4604 if(IsService(mn
->user
))
4607 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4608 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4610 if(!(mn
->modes
& MODE_CHANOP
))
4612 changes
->args
[used
].mode
= MODE_CHANOP
;
4613 changes
->args
[used
++].u
.member
= mn
;
4616 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4618 if(mn
->modes
& MODE_CHANOP
)
4620 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4621 changes
->args
[used
++].u
.member
= mn
;
4623 if(!(mn
->modes
& MODE_HALFOP
))
4625 changes
->args
[used
].mode
= MODE_HALFOP
;
4626 changes
->args
[used
++].u
.member
= mn
;
4628 /* why cant halfops keep voice
4629 if(mn->modes & MODE_VOICE)
4631 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4632 changes->args[used++].u.member = mn;
4636 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4638 if(mn
->modes
& MODE_CHANOP
)
4640 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4641 changes
->args
[used
++].u
.member
= mn
;
4643 if(mn
->modes
& MODE_HALFOP
)
4645 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4646 changes
->args
[used
++].u
.member
= mn
;
4648 if(!(mn
->modes
& MODE_VOICE
))
4650 changes
->args
[used
].mode
= MODE_VOICE
;
4651 changes
->args
[used
++].u
.member
= mn
;
4658 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4659 changes
->args
[used
++].u
.member
= mn
;
4663 changes
->argc
= used
;
4664 modcmd_chanmode_announce(changes
);
4665 mod_chanmode_free(changes
);
4666 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4670 static CHANSERV_FUNC(cmd_seen
)
4672 struct userData
*uData
;
4673 struct handle_info
*handle
;
4674 char seen
[INTERVALLEN
];
4678 if(!irccasecmp(argv
[1], chanserv
->nick
))
4680 reply("CSMSG_IS_CHANSERV");
4684 if(!(handle
= get_handle_info(argv
[1])))
4686 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4690 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4692 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4697 reply("CSMSG_USER_PRESENT", handle
->handle
);
4698 else if(uData
->seen
)
4699 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4701 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4703 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4704 reply("CSMSG_USER_VACATION", handle
->handle
);
4709 static MODCMD_FUNC(cmd_names
)
4711 struct userNode
*targ
;
4712 struct userData
*targData
;
4713 unsigned int ii
, pos
;
4716 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4718 targ
= channel
->members
.list
[ii
]->user
;
4719 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4722 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4725 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4729 if(IsUserSuspended(targData
))
4731 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4734 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4735 reply("CSMSG_END_NAMES", channel
->name
);
4740 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4742 switch(ntype
->visible_type
)
4744 case NOTE_VIS_ALL
: return 1;
4745 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4746 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4751 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4753 struct userData
*uData
;
4755 switch(ntype
->set_access_type
)
4757 case NOTE_SET_CHANNEL_ACCESS
:
4758 if(!user
->handle_info
)
4760 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4762 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4763 case NOTE_SET_CHANNEL_SETTER
:
4764 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4765 case NOTE_SET_PRIVILEGED
: default:
4766 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4770 static CHANSERV_FUNC(cmd_note
)
4772 struct chanData
*cData
;
4774 struct note_type
*ntype
;
4776 cData
= channel
->channel_info
;
4779 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4783 /* If no arguments, show all visible notes for the channel. */
4789 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4791 note
= iter_data(it
);
4792 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4795 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4796 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4799 reply("CSMSG_NOTELIST_END", channel
->name
);
4801 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4803 /* If one argument, show the named note. */
4806 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4807 && note_type_visible_to_user(cData
, note
->type
, user
))
4809 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4811 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4812 && note_type_visible_to_user(NULL
, ntype
, user
))
4814 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4819 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4823 /* Assume they're trying to set a note. */
4827 ntype
= dict_find(note_types
, argv
[1], NULL
);
4830 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4833 else if(note_type_settable_by_user(channel
, ntype
, user
))
4835 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4836 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4837 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4838 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4839 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4841 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4843 /* The note is viewable to staff only, so return 0
4844 to keep the invocation from getting logged (or
4845 regular users can see it in !events). */
4851 reply("CSMSG_NO_ACCESS");
4858 static CHANSERV_FUNC(cmd_delnote
)
4863 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4864 || !note_type_settable_by_user(channel
, note
->type
, user
))
4866 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4869 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4870 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4874 static CHANSERV_FUNC(cmd_last
)
4880 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
4882 if(numoflines
< 1 || numoflines
> 200)
4884 reply("CSMSG_LAST_INVALID");
4887 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
4891 static CHANSERV_FUNC(cmd_events
)
4893 struct logSearch discrim
;
4894 struct logReport report
;
4895 unsigned int matches
, limit
;
4897 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4898 if(limit
< 1 || limit
> 200)
4901 memset(&discrim
, 0, sizeof(discrim
));
4902 discrim
.masks
.bot
= chanserv
;
4903 discrim
.masks
.channel_name
= channel
->name
;
4905 discrim
.masks
.command
= argv
[2];
4906 discrim
.limit
= limit
;
4907 discrim
.max_time
= INT_MAX
;
4908 discrim
.severities
= 1 << LOG_COMMAND
;
4909 report
.reporter
= chanserv
;
4911 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4913 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4915 reply("MSG_MATCH_COUNT", matches
);
4917 reply("MSG_NO_MATCHES");
4921 static CHANSERV_FUNC(cmd_say
)
4927 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4928 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4930 else if(GetUserH(argv
[1]))
4933 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4934 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4938 reply("MSG_NOT_TARGET_NAME");
4944 static CHANSERV_FUNC(cmd_emote
)
4950 /* CTCP is so annoying. */
4951 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4952 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4954 else if(GetUserH(argv
[1]))
4956 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4957 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4961 reply("MSG_NOT_TARGET_NAME");
4967 struct channelList
*
4968 chanserv_support_channels(void)
4970 return &chanserv_conf
.support_channels
;
4973 static CHANSERV_FUNC(cmd_expire
)
4975 int channel_count
= registered_channels
;
4976 expire_channels(NULL
);
4977 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4982 chanserv_expire_suspension(void *data
)
4984 struct suspended
*suspended
= data
;
4985 struct chanNode
*channel
;
4987 if(!suspended
->expires
|| (now
< suspended
->expires
))
4988 suspended
->revoked
= now
;
4989 channel
= suspended
->cData
->channel
;
4990 suspended
->cData
->channel
= channel
;
4991 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4992 if(!IsOffChannel(suspended
->cData
))
4994 struct mod_chanmode change
;
4995 mod_chanmode_init(&change
);
4997 change
.args
[0].mode
= MODE_CHANOP
;
4998 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4999 mod_chanmode_announce(chanserv
, channel
, &change
);
5003 static CHANSERV_FUNC(cmd_csuspend
)
5005 struct suspended
*suspended
;
5006 char reason
[MAXLEN
];
5007 time_t expiry
, duration
;
5008 struct userData
*uData
;
5012 if(IsProtected(channel
->channel_info
))
5014 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5018 if(argv
[1][0] == '!')
5020 else if(IsSuspended(channel
->channel_info
))
5022 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5023 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5027 if(!strcmp(argv
[1], "0"))
5029 else if((duration
= ParseInterval(argv
[1])))
5030 expiry
= now
+ duration
;
5033 reply("MSG_INVALID_DURATION", argv
[1]);
5037 unsplit_string(argv
+ 2, argc
- 2, reason
);
5039 suspended
= calloc(1, sizeof(*suspended
));
5040 suspended
->revoked
= 0;
5041 suspended
->issued
= now
;
5042 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5043 suspended
->expires
= expiry
;
5044 suspended
->reason
= strdup(reason
);
5045 suspended
->cData
= channel
->channel_info
;
5046 suspended
->previous
= suspended
->cData
->suspended
;
5047 suspended
->cData
->suspended
= suspended
;
5049 if(suspended
->expires
)
5050 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5052 if(IsSuspended(channel
->channel_info
))
5054 suspended
->previous
->revoked
= now
;
5055 if(suspended
->previous
->expires
)
5056 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5057 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5058 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5062 /* Mark all users in channel as absent. */
5063 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5072 /* Mark the channel as suspended, then part. */
5073 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5074 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5075 reply("CSMSG_SUSPENDED", channel
->name
);
5076 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5077 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5082 static CHANSERV_FUNC(cmd_cunsuspend
)
5084 struct suspended
*suspended
;
5085 char message
[MAXLEN
];
5087 if(!IsSuspended(channel
->channel_info
))
5089 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5093 suspended
= channel
->channel_info
->suspended
;
5095 /* Expire the suspension and join ChanServ to the channel. */
5096 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5097 chanserv_expire_suspension(suspended
);
5098 reply("CSMSG_UNSUSPENDED", channel
->name
);
5099 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5100 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5104 typedef struct chanservSearch
5112 unsigned long flags
;
5116 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5119 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5124 search
= malloc(sizeof(struct chanservSearch
));
5125 memset(search
, 0, sizeof(*search
));
5128 for(i
= 0; i
< argc
; i
++)
5130 /* Assume all criteria require arguments. */
5133 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5137 if(!irccasecmp(argv
[i
], "name"))
5138 search
->name
= argv
[++i
];
5139 else if(!irccasecmp(argv
[i
], "registrar"))
5140 search
->registrar
= argv
[++i
];
5141 else if(!irccasecmp(argv
[i
], "unvisited"))
5142 search
->unvisited
= ParseInterval(argv
[++i
]);
5143 else if(!irccasecmp(argv
[i
], "registered"))
5144 search
->registered
= ParseInterval(argv
[++i
]);
5145 else if(!irccasecmp(argv
[i
], "flags"))
5148 if(!irccasecmp(argv
[i
], "nodelete"))
5149 search
->flags
|= CHANNEL_NODELETE
;
5150 else if(!irccasecmp(argv
[i
], "suspended"))
5151 search
->flags
|= CHANNEL_SUSPENDED
;
5154 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5158 else if(!irccasecmp(argv
[i
], "limit"))
5159 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5162 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5167 if(search
->name
&& !strcmp(search
->name
, "*"))
5169 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5170 search
->registrar
= 0;
5179 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5181 const char *name
= channel
->channel
->name
;
5182 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5183 (search
->registrar
&& !channel
->registrar
) ||
5184 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5185 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5186 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5187 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5194 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5196 struct chanData
*channel
;
5197 unsigned int matches
= 0;
5199 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5201 if(!chanserv_channel_match(channel
, search
))
5211 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5216 search_print(struct chanData
*channel
, void *data
)
5218 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5221 static CHANSERV_FUNC(cmd_search
)
5224 unsigned int matches
;
5225 channel_search_func action
;
5229 if(!irccasecmp(argv
[1], "count"))
5230 action
= search_count
;
5231 else if(!irccasecmp(argv
[1], "print"))
5232 action
= search_print
;
5235 reply("CSMSG_ACTION_INVALID", argv
[1]);
5239 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5243 if(action
== search_count
)
5244 search
->limit
= INT_MAX
;
5246 if(action
== search_print
)
5248 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5252 matches
= chanserv_channel_search(search
, action
, user
);
5255 reply("MSG_MATCH_COUNT", matches
);
5257 reply("MSG_NO_MATCHES");
5263 static CHANSERV_FUNC(cmd_unvisited
)
5265 struct chanData
*cData
;
5266 time_t interval
= chanserv_conf
.channel_expire_delay
;
5267 char buffer
[INTERVALLEN
];
5268 unsigned int limit
= 25, matches
= 0;
5272 interval
= ParseInterval(argv
[1]);
5274 limit
= atoi(argv
[2]);
5277 intervalString(buffer
, interval
, user
->handle_info
);
5278 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5280 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5282 if((now
- cData
->visited
) < interval
)
5285 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5286 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5293 static MODCMD_FUNC(chan_opt_defaulttopic
)
5299 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5301 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5305 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5307 free(channel
->channel_info
->topic
);
5308 if(topic
[0] == '*' && topic
[1] == 0)
5310 topic
= channel
->channel_info
->topic
= NULL
;
5314 topic
= channel
->channel_info
->topic
= strdup(topic
);
5315 if(channel
->channel_info
->topic_mask
5316 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5317 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5319 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5322 if(channel
->channel_info
->topic
)
5323 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5325 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5329 static MODCMD_FUNC(chan_opt_topicmask
)
5333 struct chanData
*cData
= channel
->channel_info
;
5336 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5338 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5342 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5344 if(cData
->topic_mask
)
5345 free(cData
->topic_mask
);
5346 if(mask
[0] == '*' && mask
[1] == 0)
5348 cData
->topic_mask
= 0;
5352 cData
->topic_mask
= strdup(mask
);
5354 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5355 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5356 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5360 if(channel
->channel_info
->topic_mask
)
5361 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5363 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5367 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5371 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5375 if(greeting
[0] == '*' && greeting
[1] == 0)
5379 unsigned int length
= strlen(greeting
);
5380 if(length
> chanserv_conf
.greeting_length
)
5382 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5385 *data
= strdup(greeting
);
5394 reply(name
, user_find_message(user
, "MSG_NONE"));
5398 static MODCMD_FUNC(chan_opt_greeting
)
5400 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5403 static MODCMD_FUNC(chan_opt_usergreeting
)
5405 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5408 static MODCMD_FUNC(chan_opt_modes
)
5410 struct mod_chanmode
*new_modes
;
5411 char modes
[MODELEN
];
5415 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5417 reply("CSMSG_NO_ACCESS");
5420 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5422 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5424 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5426 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5429 else if(new_modes
->argc
> 1)
5431 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5432 mod_chanmode_free(new_modes
);
5437 channel
->channel_info
->modes
= *new_modes
;
5438 modcmd_chanmode_announce(new_modes
);
5439 mod_chanmode_free(new_modes
);
5443 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5445 reply("CSMSG_SET_MODES", modes
);
5447 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5451 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5453 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5455 struct chanData
*cData
= channel
->channel_info
;
5460 /* Set flag according to value. */
5461 if(enabled_string(argv
[1]))
5463 cData
->flags
|= mask
;
5466 else if(disabled_string(argv
[1]))
5468 cData
->flags
&= ~mask
;
5473 reply("MSG_INVALID_BINARY", argv
[1]);
5479 /* Find current option value. */
5480 value
= (cData
->flags
& mask
) ? 1 : 0;
5484 reply(name
, user_find_message(user
, "MSG_ON"));
5486 reply(name
, user_find_message(user
, "MSG_OFF"));
5490 static MODCMD_FUNC(chan_opt_nodelete
)
5492 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5494 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5498 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5501 static MODCMD_FUNC(chan_opt_dynlimit
)
5503 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5506 static MODCMD_FUNC(chan_opt_offchannel
)
5508 struct chanData
*cData
= channel
->channel_info
;
5513 /* Set flag according to value. */
5514 if(enabled_string(argv
[1]))
5516 if(!IsOffChannel(cData
))
5517 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5518 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5521 else if(disabled_string(argv
[1]))
5523 if(IsOffChannel(cData
))
5525 struct mod_chanmode change
;
5526 mod_chanmode_init(&change
);
5528 change
.args
[0].mode
= MODE_CHANOP
;
5529 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5530 mod_chanmode_announce(chanserv
, channel
, &change
);
5532 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5537 reply("MSG_INVALID_BINARY", argv
[1]);
5543 /* Find current option value. */
5544 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5548 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5550 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5554 static MODCMD_FUNC(chan_opt_defaults
)
5556 struct userData
*uData
;
5557 struct chanData
*cData
;
5558 const char *confirm
;
5559 enum levelOption lvlOpt
;
5560 enum charOption chOpt
;
5562 cData
= channel
->channel_info
;
5563 uData
= GetChannelUser(cData
, user
->handle_info
);
5564 if(!uData
|| (uData
->access
< UL_OWNER
))
5566 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5569 confirm
= make_confirmation_string(uData
);
5570 if((argc
< 2) || strcmp(argv
[1], confirm
))
5572 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5575 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5576 cData
->modes
= chanserv_conf
.default_modes
;
5577 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5578 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5579 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5580 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5581 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5586 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5588 struct chanData
*cData
= channel
->channel_info
;
5589 struct userData
*uData
;
5590 unsigned short value
;
5594 if(!check_user_level(channel
, user
, option
, 1, 1))
5596 reply("CSMSG_CANNOT_SET");
5599 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5600 if(!value
&& strcmp(argv
[1], "0"))
5602 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5605 uData
= GetChannelUser(cData
, user
->handle_info
);
5606 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5608 reply("CSMSG_BAD_SETLEVEL");
5613 /* removing these level sets..
5615 if(value > cData->lvlOpts[lvlGiveOps])
5617 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5621 case lvlGiveHalfOps:
5622 if(value < cData->lvlOpts[lvlGiveVoice])
5624 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5629 if(value < cData->lvlOpts[lvlGiveVoice])
5631 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5637 /* This test only applies to owners, since non-owners
5638 * trying to set an option to above their level get caught
5639 * by the CSMSG_BAD_SETLEVEL test above.
5641 if(value
> uData
->access
)
5643 reply("CSMSG_BAD_SETTERS");
5650 cData
->lvlOpts
[option
] = value
;
5652 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5656 static MODCMD_FUNC(chan_opt_enfops
)
5658 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5661 static MODCMD_FUNC(chan_opt_enfhalfops
)
5663 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5666 static MODCMD_FUNC(chan_opt_giveops)
5668 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5671 static MODCMD_FUNC(chan_opt_givehalfops)
5673 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5676 static MODCMD_FUNC(chan_opt_enfmodes
)
5678 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5681 static MODCMD_FUNC(chan_opt_enftopic
)
5683 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5686 static MODCMD_FUNC(chan_opt_pubcmd
)
5688 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5691 static MODCMD_FUNC(chan_opt_setters
)
5693 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5696 static MODCMD_FUNC(chan_opt_userinfo
)
5698 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5702 static MODCMD_FUNC(chan_opt_givevoice)
5704 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5708 static MODCMD_FUNC(chan_opt_topicsnarf
)
5710 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5713 static MODCMD_FUNC(chan_opt_inviteme
)
5715 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5718 /* TODO: Make look like this when no args are
5720 * -X3- -------------------------------
5721 * -X3- BanTimeout: Bans are removed:
5722 * -X3- ----- * indicates current -----
5723 * -X3- 0: [*] Never.
5724 * -X3- 1: [ ] After 10 minutes.
5725 * -X3- 2: [ ] After 2 hours.
5726 * -X3- 3: [ ] After 4 hours.
5727 * -X3- 4: [ ] After 24 hours.
5728 * -X3- 5: [ ] After one week.
5729 * -X3- ------------- End -------------
5732 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5734 struct chanData
*cData
= channel
->channel_info
;
5735 int count
= charOptions
[option
].count
, index
;
5739 index
= atoi(argv
[1]);
5741 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5743 reply("CSMSG_INVALID_NUMERIC", index
);
5744 /* Show possible values. */
5745 for(index
= 0; index
< count
; index
++)
5746 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5750 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5754 /* Find current option value. */
5757 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5761 /* Somehow, the option value is corrupt; reset it to the default. */
5762 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5767 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5771 static MODCMD_FUNC(chan_opt_voice
)
5773 return channel_multiple_option(chVoice
, CSFUNC_ARGS
);
5776 static MODCMD_FUNC(chan_opt_protect
)
5778 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5781 static MODCMD_FUNC(chan_opt_toys
)
5783 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5786 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5788 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5791 static MODCMD_FUNC(chan_opt_bantimeout
)
5793 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
5796 static MODCMD_FUNC(chan_opt_topicrefresh
)
5798 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5801 static struct svccmd_list set_shows_list
;
5804 handle_svccmd_unbind(struct svccmd
*target
) {
5806 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5807 if(target
== set_shows_list
.list
[ii
])
5808 set_shows_list
.used
= 0;
5811 static CHANSERV_FUNC(cmd_set
)
5813 struct svccmd
*subcmd
;
5817 /* Check if we need to (re-)initialize set_shows_list. */
5818 if(!set_shows_list
.used
)
5820 if(!set_shows_list
.size
)
5822 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5823 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5825 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5827 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5828 sprintf(buf
, "%s %s", argv
[0], name
);
5829 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5832 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5835 svccmd_list_append(&set_shows_list
, subcmd
);
5841 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5843 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5845 subcmd
= set_shows_list
.list
[ii
];
5846 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5848 reply("CSMSG_CHANNEL_OPTIONS_END");
5852 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5853 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5856 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5859 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5861 reply("CSMSG_NO_ACCESS");
5865 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5869 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5871 struct userData
*uData
;
5873 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5876 reply("CSMSG_NOT_USER", channel
->name
);
5882 /* Just show current option value. */
5884 else if(enabled_string(argv
[1]))
5886 uData
->flags
|= mask
;
5888 else if(disabled_string(argv
[1]))
5890 uData
->flags
&= ~mask
;
5894 reply("MSG_INVALID_BINARY", argv
[1]);
5898 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5902 static MODCMD_FUNC(user_opt_autoop
)
5904 struct userData
*uData
;
5906 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5909 reply("CSMSG_NOT_USER", channel
->name
);
5912 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5913 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5915 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5916 /* TODO: add halfops error message? or is the op one generic enough? */
5919 static MODCMD_FUNC(user_opt_autoinvite
)
5921 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5924 static MODCMD_FUNC(user_opt_info
)
5926 struct userData
*uData
;
5929 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5933 /* If they got past the command restrictions (which require access)
5934 * but fail this test, we have some fool with security override on.
5936 reply("CSMSG_NOT_USER", channel
->name
);
5943 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5944 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5946 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5949 bp
= strcspn(infoline
, "\001");
5952 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5957 if(infoline
[0] == '*' && infoline
[1] == 0)
5960 uData
->info
= strdup(infoline
);
5963 reply("CSMSG_USET_INFO", uData
->info
);
5965 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5969 struct svccmd_list uset_shows_list
;
5971 static CHANSERV_FUNC(cmd_uset
)
5973 struct svccmd
*subcmd
;
5977 /* Check if we need to (re-)initialize uset_shows_list. */
5978 if(!uset_shows_list
.used
)
5982 "AutoOp", "AutoInvite", "Info"
5985 if(!uset_shows_list
.size
)
5987 uset_shows_list
.size
= ArrayLength(options
);
5988 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5990 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5992 const char *name
= options
[ii
];
5993 sprintf(buf
, "%s %s", argv
[0], name
);
5994 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5997 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6000 svccmd_list_append(&uset_shows_list
, subcmd
);
6006 /* Do this so options are presented in a consistent order. */
6007 reply("CSMSG_USER_OPTIONS");
6008 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6009 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6013 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6014 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6017 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6021 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6024 static CHANSERV_FUNC(cmd_giveownership
)
6026 struct handle_info
*new_owner_hi
;
6027 struct userData
*new_owner
, *curr_user
;
6028 struct chanData
*cData
= channel
->channel_info
;
6029 struct do_not_register
*dnr
;
6031 unsigned short co_access
;
6032 char reason
[MAXLEN
];
6035 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6036 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6037 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6039 struct userData
*owner
= NULL
;
6040 for(curr_user
= channel
->channel_info
->users
;
6042 curr_user
= curr_user
->next
)
6044 if(curr_user
->access
!= UL_OWNER
)
6048 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6055 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6057 char delay
[INTERVALLEN
];
6058 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6059 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6062 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6064 if(new_owner_hi
== user
->handle_info
)
6066 reply("CSMSG_NO_TRANSFER_SELF");
6069 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6074 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6078 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6082 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6084 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6087 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6088 if(!IsHelping(user
))
6089 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6091 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6094 if(new_owner
->access
>= UL_COOWNER
)
6095 co_access
= new_owner
->access
;
6097 co_access
= UL_COOWNER
;
6098 new_owner
->access
= UL_OWNER
;
6100 curr_user
->access
= co_access
;
6101 cData
->ownerTransfer
= now
;
6102 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6103 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6104 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6108 static CHANSERV_FUNC(cmd_suspend
)
6110 struct handle_info
*hi
;
6111 struct userData
*self
, *target
;
6114 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6115 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6116 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6118 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6121 if(target
->access
>= self
->access
)
6123 reply("MSG_USER_OUTRANKED", hi
->handle
);
6126 if(target
->flags
& USER_SUSPENDED
)
6128 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6133 target
->present
= 0;
6136 target
->flags
|= USER_SUSPENDED
;
6137 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6141 static CHANSERV_FUNC(cmd_unsuspend
)
6143 struct handle_info
*hi
;
6144 struct userData
*self
, *target
;
6147 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6148 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6149 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6151 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6154 if(target
->access
>= self
->access
)
6156 reply("MSG_USER_OUTRANKED", hi
->handle
);
6159 if(!(target
->flags
& USER_SUSPENDED
))
6161 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6164 target
->flags
&= ~USER_SUSPENDED
;
6165 scan_user_presence(target
, NULL
);
6166 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6170 static MODCMD_FUNC(cmd_deleteme
)
6172 struct handle_info
*hi
;
6173 struct userData
*target
;
6174 const char *confirm_string
;
6175 unsigned short access
;
6178 hi
= user
->handle_info
;
6179 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6181 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6184 if(target
->access
== UL_OWNER
)
6186 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6189 confirm_string
= make_confirmation_string(target
);
6190 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6192 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6195 access
= target
->access
;
6196 channel_name
= strdup(channel
->name
);
6197 del_channel_user(target
, 1);
6198 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6204 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6206 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6207 struct chanData
*cData
;
6210 for(cData
= channelList
; cData
; cData
= cData
->next
)
6212 if(IsSuspended(cData
))
6214 opt
= cData
->chOpts
[chTopicRefresh
];
6217 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6220 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6221 cData
->last_refresh
= refresh_num
;
6223 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6226 static CHANSERV_FUNC(cmd_unf
)
6230 char response
[MAXLEN
];
6231 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6232 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6233 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6236 reply("CSMSG_UNF_RESPONSE");
6240 static CHANSERV_FUNC(cmd_ping
)
6244 char response
[MAXLEN
];
6245 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6246 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6247 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6250 reply("CSMSG_PING_RESPONSE");
6254 static CHANSERV_FUNC(cmd_wut
)
6258 char response
[MAXLEN
];
6259 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6260 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6261 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6264 reply("CSMSG_WUT_RESPONSE");
6269 static CHANSERV_FUNC(cmd_8ball
)
6271 unsigned int i
, j
, accum
;
6276 for(i
=1; i
<argc
; i
++)
6277 for(j
=0; argv
[i
][j
]; j
++)
6278 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6279 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6282 char response
[MAXLEN
];
6283 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6284 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6287 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6291 #else /* Use cool 8ball instead */
6293 void eightball(char *outcome
, int method
, unsigned int seed
)
6297 #define NUMOFCOLORS 18
6298 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6299 "white", "black", "grey", "brown",
6300 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6301 "fuchsia","turquoise","magenta", "cyan"};
6302 #define NUMOFLOCATIONS 50
6303 char balllocations
[50][55] = {
6304 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6305 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6306 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6307 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6308 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6309 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6310 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6311 "your bra", "your hair", "your bed", "the couch", "the wall",
6312 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6313 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6314 #define NUMOFPREPS 15
6315 char ballpreps
[50][50] = {
6316 "Near", "Somewhere near", "In", "In", "In",
6317 "In", "Hiding in", "Under", "Next to", "Over",
6318 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6319 #define NUMOFNUMS 34
6320 char ballnums
[50][50] = {
6321 "A hundred", "A thousand", "A few", "42",
6322 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6323 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6324 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6326 #define NUMOFMULTS 8
6327 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6330 * 0: normal (Not used in x3)
6337 if (method
== 1) /* A Color */
6341 answer
= (rand() % 12); /* Make sure this is the # of entries */
6344 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6346 case 1: strcpy(tmp
, "Sort of a light %s color.");
6348 case 2: strcpy(tmp
, "Dark and dreary %s.");
6350 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6352 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6354 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6356 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6358 case 10: strcpy(tmp
, "Solid %s.");
6360 case 11: strcpy(tmp
, "Transparent %s.");
6362 default: strcpy(outcome
, "An invalid random number was generated.");
6365 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6368 else if (method
== 2) /* Location */
6370 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6372 else if (method
== 3) /* Number of ___ */
6374 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6378 //Debug(DBGWARNING, "Error in 8ball.");
6383 static CHANSERV_FUNC(cmd_8ball
)
6385 char *word1
, *word2
, *word3
;
6386 static char eb
[MAXLEN
];
6387 unsigned int accum
, i
, j
;
6391 for(i
=1; i
<argc
; i
++)
6392 for(j
=0; argv
[i
][j
]; j
++)
6393 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6395 accum
+= time(NULL
)/3600;
6397 word2
= argc
>2?argv
[2]:"";
6398 word3
= argc
>3?argv
[3]:"";
6401 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6402 eightball(eb
, 1, accum
);
6403 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6404 eightball(eb
, 1, accum
);
6405 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6406 eightball(eb
, 1, accum
);
6407 /*** LOCATION *****/
6412 (strcasecmp(word1
, "where") == 0) &&
6413 (strcasecmp(word2
, "is") == 0)
6417 strcasecmp(word1
, "where's") == 0
6420 eightball(eb
, 2, accum
);
6422 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6423 eightball(eb
, 3, accum
);
6427 /* Generic 8ball question.. so pull from x3.conf srvx style */
6430 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6433 char response
[MAXLEN
];
6434 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6435 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6438 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6444 char response
[MAXLEN
];
6445 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6446 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6449 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6454 static CHANSERV_FUNC(cmd_d
)
6456 unsigned long sides
, count
, modifier
, ii
, total
;
6457 char response
[MAXLEN
], *sep
;
6461 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6471 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6472 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6476 else if((sep
[0] == '-') && isdigit(sep
[1]))
6477 modifier
= strtoul(sep
, NULL
, 10);
6478 else if((sep
[0] == '+') && isdigit(sep
[1]))
6479 modifier
= strtoul(sep
+1, NULL
, 10);
6486 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6491 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6494 for(total
= ii
= 0; ii
< count
; ++ii
)
6495 total
+= (rand() % sides
) + 1;
6498 if((count
> 1) || modifier
)
6500 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6501 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6505 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6506 sprintf(response
, fmt
, total
, sides
);
6509 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6511 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6515 static CHANSERV_FUNC(cmd_huggle
)
6517 /* CTCP must be via PRIVMSG, never notice */
6519 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6521 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6525 static CHANSERV_FUNC(cmd_calc
)
6527 char response
[MAXLEN
];
6530 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6533 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6535 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6540 chanserv_adjust_limit(void *data
)
6542 struct mod_chanmode change
;
6543 struct chanData
*cData
= data
;
6544 struct chanNode
*channel
= cData
->channel
;
6547 if(IsSuspended(cData
))
6550 cData
->limitAdjusted
= now
;
6551 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6552 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6554 if(limit
> cData
->modes
.new_limit
)
6555 limit
= cData
->modes
.new_limit
;
6556 else if(limit
== cData
->modes
.new_limit
)
6560 mod_chanmode_init(&change
);
6561 change
.modes_set
= MODE_LIMIT
;
6562 change
.new_limit
= limit
;
6563 mod_chanmode_announce(chanserv
, channel
, &change
);
6567 handle_new_channel(struct chanNode
*channel
)
6569 struct chanData
*cData
;
6571 if(!(cData
= channel
->channel_info
))
6574 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6575 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6577 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6578 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6581 /* Welcome to my worst nightmare. Warning: Read (or modify)
6582 the code below at your own risk. */
6584 handle_join(struct modeNode
*mNode
)
6586 struct mod_chanmode change
;
6587 struct userNode
*user
= mNode
->user
;
6588 struct chanNode
*channel
= mNode
->channel
;
6589 struct chanData
*cData
;
6590 struct userData
*uData
= NULL
;
6591 struct banData
*bData
;
6592 struct handle_info
*handle
;
6593 unsigned int modes
= 0, info
= 0;
6596 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6599 cData
= channel
->channel_info
;
6600 if(channel
->members
.used
> cData
->max
)
6601 cData
->max
= channel
->members
.used
;
6603 /* Check for bans. If they're joining through a ban, one of two
6605 * 1: Join during a netburst, by riding the break. Kick them
6606 * unless they have ops or voice in the channel.
6607 * 2: They're allowed to join through the ban (an invite in
6608 * ircu2.10, or a +e on Hybrid, or something).
6609 * If they're not joining through a ban, and the banlist is not
6610 * full, see if they're on the banlist for the channel. If so,
6613 /* This is really, really stupid. not all banned people are kicked.
6614 * sometimes we like to leave them unkicked.
6615 * I tried to explain this to the srvx developers and
6616 * got insulted.. hence one reason for this fork.
6618 if(user->uplink->burst && !mNode->modes)
6621 for(ii = 0; ii < channel->banlist.used; ii++)
6623 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6625 ** Riding a netburst. Naughty. **
6626 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6633 mod_chanmode_init(&change
);
6635 if(channel
->banlist
.used
< MAXBANS
)
6637 /* Not joining through a ban. */
6638 for(bData
= cData
->bans
;
6639 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6640 bData
= bData
->next
);
6644 char kick_reason
[MAXLEN
];
6645 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6647 bData
->triggered
= now
;
6648 if(bData
!= cData
->bans
)
6650 /* Shuffle the ban to the head of the list. */
6652 bData
->next
->prev
= bData
->prev
;
6654 bData
->prev
->next
= bData
->next
;
6657 bData
->next
= cData
->bans
;
6660 cData
->bans
->prev
= bData
;
6661 cData
->bans
= bData
;
6664 change
.args
[0].mode
= MODE_BAN
;
6665 change
.args
[0].u
.hostmask
= bData
->mask
;
6666 mod_chanmode_announce(chanserv
, channel
, &change
);
6667 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6672 /* ChanServ will not modify the limits in join-flooded channels.
6673 It will also skip DynLimit processing when the user (or srvx)
6674 is bursting in, because there are likely more incoming. */
6675 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6676 && !user
->uplink
->burst
6677 && !channel
->join_flooded
6678 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6680 /* The user count has begun "bumping" into the channel limit,
6681 so set a timer to raise the limit a bit. Any previous
6682 timers are removed so three incoming users within the delay
6683 results in one limit change, not three. */
6685 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6686 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6689 if(channel
->join_flooded
)
6691 /* don't automatically give non users ops or voice during a join flood */
6693 /* EVERYONE is to get voice */
6694 else if(cData
->chOpts
[chVoice
] == 'a')
6695 modes
|= MODE_VOICE
;
6697 greeting
= cData
->greeting
;
6698 if(user
->handle_info
)
6700 handle
= user
->handle_info
;
6702 if(IsHelper(user
) && !IsHelping(user
))
6705 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6707 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6709 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6715 uData
= GetTrueChannelAccess(cData
, handle
);
6716 if(uData
&& !IsUserSuspended(uData
))
6718 /* non users getting voice are handled above. */
6719 if(IsUserAutoOp(uData
))
6721 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6722 modes
|= MODE_CHANOP
;
6723 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6724 modes
|= MODE_HALFOP
;
6725 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chVoice
] == 'p')
6726 modes
|= MODE_VOICE
;
6728 if(uData
->access
>= UL_PRESENT
)
6729 cData
->visited
= now
;
6730 if(cData
->user_greeting
)
6731 greeting
= cData
->user_greeting
;
6733 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6734 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6741 if(!user
->uplink
->burst
)
6745 if(modes
& MODE_CHANOP
) {
6746 modes
&= ~MODE_HALFOP
;
6747 modes
&= ~MODE_VOICE
;
6749 change
.args
[0].mode
= modes
;
6750 change
.args
[0].u
.member
= mNode
;
6751 mod_chanmode_announce(chanserv
, channel
, &change
);
6753 if(greeting
&& !user
->uplink
->burst
)
6754 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6756 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6762 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6764 struct mod_chanmode change
;
6765 struct userData
*channel
;
6766 unsigned int ii
, jj
;
6768 if(!user
->handle_info
)
6771 mod_chanmode_init(&change
);
6773 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6775 struct chanNode
*cn
;
6776 struct modeNode
*mn
;
6777 if(IsUserSuspended(channel
)
6778 || IsSuspended(channel
->channel
)
6779 || !(cn
= channel
->channel
->channel
))
6782 mn
= GetUserMode(cn
, user
);
6785 if(!IsUserSuspended(channel
)
6786 && IsUserAutoInvite(channel
)
6787 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6789 && !user
->uplink
->burst
)
6790 irc_invite(chanserv
, user
, cn
);
6794 if(channel
->access
>= UL_PRESENT
)
6795 channel
->channel
->visited
= now
;
6797 if(IsUserAutoOp(channel
))
6799 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6800 change
.args
[0].mode
= MODE_CHANOP
;
6801 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6802 change
.args
[0].mode
= MODE_HALFOP
;
6803 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6804 change
.args
[0].mode
= MODE_VOICE
;
6806 change
.args
[0].mode
= 0;
6807 change
.args
[0].u
.member
= mn
;
6808 if(change
.args
[0].mode
)
6809 mod_chanmode_announce(chanserv
, cn
, &change
);
6812 channel
->seen
= now
;
6813 channel
->present
= 1;
6816 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6818 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6819 struct banData
*ban
;
6821 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6822 || !channel
->channel_info
6823 || IsSuspended(channel
->channel_info
))
6825 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6826 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6828 if(jj
< channel
->banlist
.used
)
6830 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6832 char kick_reason
[MAXLEN
];
6833 if(!user_matches_glob(user
, ban
->mask
, 1))
6835 change
.args
[0].mode
= MODE_BAN
;
6836 change
.args
[0].u
.hostmask
= ban
->mask
;
6837 mod_chanmode_announce(chanserv
, channel
, &change
);
6838 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6839 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6840 ban
->triggered
= now
;
6845 if(IsSupportHelper(user
))
6847 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6849 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6851 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6859 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6861 struct chanData
*cData
;
6862 struct userData
*uData
;
6864 cData
= mn
->channel
->channel_info
;
6865 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6868 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6870 /* Allow for a bit of padding so that the limit doesn't
6871 track the user count exactly, which could get annoying. */
6872 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6874 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6875 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6879 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6881 scan_user_presence(uData
, mn
->user
);
6885 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6887 unsigned int ii
, jj
;
6888 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6890 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6891 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6893 if(jj
< mn
->user
->channels
.used
)
6896 if(ii
== chanserv_conf
.support_channels
.used
)
6897 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6902 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6904 struct userData
*uData
;
6906 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6907 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6908 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6911 if(protect_user(victim
, kicker
, channel
->channel_info
))
6913 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
6914 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6917 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6922 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6924 struct chanData
*cData
;
6926 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6929 cData
= channel
->channel_info
;
6930 if(bad_topic(channel
, user
, channel
->topic
))
6931 { /* User doesnt have privs to set topics. Undo it */
6932 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6933 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6936 /* If there is a topic mask set, and the new topic doesnt match,
6937 * set the topic to mask + new_topic */
6938 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6940 char new_topic
[TOPICLEN
+1];
6941 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6944 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6945 /* and fall through to topicsnarf code below.. */
6947 else /* Topic couldnt fit into mask, was too long */
6949 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6950 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6951 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6955 /* With topicsnarf, grab the topic and save it as the default topic. */
6956 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6959 cData
->topic
= strdup(channel
->topic
);
6965 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6967 struct mod_chanmode
*bounce
= NULL
;
6968 unsigned int bnc
, ii
;
6971 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6974 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6975 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6977 char correct
[MAXLEN
];
6978 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6979 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6980 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6982 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6984 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6986 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6987 if(!protect_user(victim
, user
, channel
->channel_info
))
6990 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6993 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6994 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6995 if(bounce
->args
[bnc
].u
.member
)
6999 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7000 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7002 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7004 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7006 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7007 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7010 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7011 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7012 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7015 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7017 const char *ban
= change
->args
[ii
].u
.hostmask
;
7018 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7021 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7022 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7023 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7025 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7030 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7031 mod_chanmode_announce(chanserv
, channel
, bounce
);
7032 for(ii
= 0; ii
< change
->argc
; ++ii
)
7033 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7034 free((char*)bounce
->args
[ii
].u
.hostmask
);
7035 mod_chanmode_free(bounce
);
7040 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7042 struct chanNode
*channel
;
7043 struct banData
*bData
;
7044 struct mod_chanmode change
;
7045 unsigned int ii
, jj
;
7046 char kick_reason
[MAXLEN
];
7048 mod_chanmode_init(&change
);
7050 change
.args
[0].mode
= MODE_BAN
;
7051 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7053 channel
= user
->channels
.list
[ii
]->channel
;
7054 /* Need not check for bans if they're opped or voiced. */
7055 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7057 /* Need not check for bans unless channel registration is active. */
7058 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7060 /* Look for a matching ban already on the channel. */
7061 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7062 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
7064 /* Need not act if we found one. */
7065 if(jj
< channel
->banlist
.used
)
7067 /* Look for a matching ban in this channel. */
7068 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7070 if(!user_matches_glob(user
, bData
->mask
, 1))
7072 change
.args
[0].u
.hostmask
= bData
->mask
;
7073 mod_chanmode_announce(chanserv
, channel
, &change
);
7074 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7075 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7076 bData
->triggered
= now
;
7077 break; /* we don't need to check any more bans in the channel */
7082 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7084 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7088 dict_remove2(handle_dnrs
, old_handle
, 1);
7089 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7090 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7095 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7097 struct userNode
*h_user
;
7099 if(handle
->channels
)
7101 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7102 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7104 while(handle
->channels
)
7105 del_channel_user(handle
->channels
, 1);
7110 handle_server_link(UNUSED_ARG(struct server
*server
))
7112 struct chanData
*cData
;
7114 for(cData
= channelList
; cData
; cData
= cData
->next
)
7116 if(!IsSuspended(cData
))
7117 cData
->may_opchan
= 1;
7118 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7119 && !cData
->channel
->join_flooded
7120 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7121 < chanserv_conf
.adjust_threshold
))
7123 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7124 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7130 chanserv_conf_read(void)
7134 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7135 struct mod_chanmode
*change
;
7136 struct string_list
*strlist
;
7137 struct chanNode
*chan
;
7140 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7142 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7145 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7146 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7147 chanserv_conf
.support_channels
.used
= 0;
7148 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7150 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7152 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7155 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7157 channelList_append(&chanserv_conf
.support_channels
, chan
);
7160 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7163 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7166 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7168 channelList_append(&chanserv_conf
.support_channels
, chan
);
7170 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7171 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7172 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7173 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7174 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7175 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7176 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7177 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7178 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7179 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7180 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7181 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7182 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7183 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7184 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7185 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7186 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7187 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7188 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7189 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7190 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7191 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7192 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7193 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7194 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7196 NickChange(chanserv
, str
, 0);
7197 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7198 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7199 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7200 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7201 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7202 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7203 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7204 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7205 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7206 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7207 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7208 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7209 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7210 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7211 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7212 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7213 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7216 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7217 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7218 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
7220 chanserv_conf
.default_modes
= *change
;
7221 mod_chanmode_free(change
);
7223 free_string_list(chanserv_conf
.set_shows
);
7224 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7226 strlist
= string_list_copy(strlist
);
7229 static const char *list
[] = {
7230 /* free form text */
7231 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7232 /* options based on user level */
7233 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
7234 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", /*"CtcpUsers", */
7235 /* multiple choice options */
7236 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7237 /* binary options */
7238 "DynLimit", "NoDelete", "BanTimeout",
7243 strlist
= alloc_string_list(ArrayLength(list
)-1);
7244 for(ii
=0; list
[ii
]; ii
++)
7245 string_list_append(strlist
, strdup(list
[ii
]));
7247 chanserv_conf
.set_shows
= strlist
;
7248 /* We don't look things up now, in case the list refers to options
7249 * defined by modules initialized after this point. Just mark the
7250 * function list as invalid, so it will be initialized.
7252 set_shows_list
.used
= 0;
7253 free_string_list(chanserv_conf
.eightball
);
7254 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7257 strlist
= string_list_copy(strlist
);
7261 strlist
= alloc_string_list(4);
7262 string_list_append(strlist
, strdup("Yes."));
7263 string_list_append(strlist
, strdup("No."));
7264 string_list_append(strlist
, strdup("Maybe so."));
7266 chanserv_conf
.eightball
= strlist
;
7267 free_string_list(chanserv_conf
.old_ban_names
);
7268 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7270 strlist
= string_list_copy(strlist
);
7272 strlist
= alloc_string_list(2);
7273 chanserv_conf
.old_ban_names
= strlist
;
7274 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7275 off_channel
= str
? atoi(str
) : 0;
7279 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7282 struct note_type
*ntype
;
7285 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7287 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7290 if(!(ntype
= chanserv_create_note_type(key
)))
7292 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7296 /* Figure out set access */
7297 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7299 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7300 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7302 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7304 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7305 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7307 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7309 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7313 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7314 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7315 ntype
->set_access
.min_opserv
= 0;
7318 /* Figure out visibility */
7319 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7320 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7321 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7322 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7323 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7324 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7325 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7326 ntype
->visible_type
= NOTE_VIS_ALL
;
7328 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7330 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7331 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7335 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7337 struct handle_info
*handle
;
7338 struct userData
*uData
;
7339 char *seen
, *inf
, *flags
;
7341 unsigned short access
;
7343 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7345 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7349 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7350 if(access
> UL_OWNER
)
7352 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7356 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7357 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7358 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7359 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7360 handle
= get_handle_info(key
);
7363 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7367 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7368 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7370 /* Upgrade: set autoop to the inverse of noautoop */
7371 if(chanserv_read_version
< 2)
7373 /* if noautoop is true, set autoop false, and vice versa */
7374 if(uData
->flags
& USER_NOAUTO_OP
)
7375 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7377 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7378 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
);
7384 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7386 struct banData
*bData
;
7387 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7388 time_t set_time
, triggered_time
, expires_time
;
7390 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7392 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7396 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7397 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7398 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7399 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7400 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7401 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7402 if (!reason
|| !owner
)
7405 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7406 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7408 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7410 expires_time
= set_time
+ atoi(s_duration
);
7414 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7417 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7420 static struct suspended
*
7421 chanserv_read_suspended(dict_t obj
)
7423 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7427 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7428 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7429 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7430 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7431 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7432 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7433 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7434 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7435 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7436 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7441 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7443 struct suspended
*suspended
;
7444 struct mod_chanmode
*modes
;
7445 struct chanNode
*cNode
;
7446 struct chanData
*cData
;
7447 struct dict
*channel
, *obj
;
7448 char *str
, *argv
[10];
7452 channel
= hir
->d
.object
;
7454 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7457 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7460 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7463 cData
= register_channel(cNode
, str
);
7466 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7470 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7472 enum levelOption lvlOpt
;
7473 enum charOption chOpt
;
7475 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7476 cData
->flags
= atoi(str
);
7478 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7480 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7482 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7483 else if(levelOptions
[lvlOpt
].old_flag
)
7485 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7486 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7488 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7492 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7494 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7496 cData
->chOpts
[chOpt
] = str
[0];
7499 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7501 enum levelOption lvlOpt
;
7502 enum charOption chOpt
;
7505 cData
->flags
= base64toint(str
, 5);
7506 count
= strlen(str
+= 5);
7507 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7510 if(levelOptions
[lvlOpt
].old_flag
)
7512 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7513 lvl
= levelOptions
[lvlOpt
].flag_value
;
7515 lvl
= levelOptions
[lvlOpt
].default_value
;
7517 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7519 case 'c': lvl
= UL_COOWNER
; break;
7520 case 'm': lvl
= UL_MANAGER
; break;
7521 case 'n': lvl
= UL_OWNER
+1; break;
7522 case 'o': lvl
= UL_OP
; break;
7523 case 'p': lvl
= UL_PEON
; break;
7524 case 'h': lvl
= UL_HALFOP
; break;
7525 case 'w': lvl
= UL_OWNER
; break;
7526 default: lvl
= 0; break;
7528 cData
->lvlOpts
[lvlOpt
] = lvl
;
7530 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7531 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7534 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7536 suspended
= chanserv_read_suspended(obj
);
7537 cData
->suspended
= suspended
;
7538 suspended
->cData
= cData
;
7539 /* We could use suspended->expires and suspended->revoked to
7540 * set the CHANNEL_SUSPENDED flag, but we don't. */
7542 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7544 suspended
= calloc(1, sizeof(*suspended
));
7545 suspended
->issued
= 0;
7546 suspended
->revoked
= 0;
7547 suspended
->suspender
= strdup(str
);
7548 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7549 suspended
->expires
= str
? atoi(str
) : 0;
7550 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7551 suspended
->reason
= strdup(str
? str
: "No reason");
7552 suspended
->previous
= NULL
;
7553 cData
->suspended
= suspended
;
7554 suspended
->cData
= cData
;
7558 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7559 suspended
= NULL
; /* to squelch a warning */
7562 if(IsSuspended(cData
)) {
7563 if(suspended
->expires
> now
)
7564 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7565 else if(suspended
->expires
)
7566 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7569 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7570 struct mod_chanmode change
;
7571 mod_chanmode_init(&change
);
7573 change
.args
[0].mode
= MODE_CHANOP
;
7574 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7575 mod_chanmode_announce(chanserv
, cNode
, &change
);
7578 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7579 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7580 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7581 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7582 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7583 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7584 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7585 cData
->max
= str
? atoi(str
) : 0;
7586 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7587 cData
->greeting
= str
? strdup(str
) : NULL
;
7588 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7589 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7590 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7591 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7592 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7593 cData
->topic
= str
? strdup(str
) : NULL
;
7595 if(!IsSuspended(cData
)
7596 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7597 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7598 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7599 cData
->modes
= *modes
;
7601 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7602 if(cData
->modes
.argc
> 1)
7603 cData
->modes
.argc
= 1;
7604 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7605 mod_chanmode_free(modes
);
7608 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7609 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7610 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7612 if(!cData
->users
&& !IsProtected(cData
))
7614 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7615 unregister_channel(cData
, "has empty user list.");
7619 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7620 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7621 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7623 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7624 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7626 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7627 struct record_data
*rd
= iter_data(it
);
7628 const char *note
, *setter
;
7630 if(rd
->type
!= RECDB_OBJECT
)
7632 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7636 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7638 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7640 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7644 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7645 if(!setter
) setter
= "<unknown>";
7646 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7654 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7656 const char *setter
, *reason
, *str
;
7657 struct do_not_register
*dnr
;
7659 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7662 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7665 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7668 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7671 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7674 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7676 dnr
->set
= atoi(str
);
7682 chanserv_version_read(struct dict
*section
)
7686 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7688 chanserv_read_version
= atoi(str
);
7689 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7693 chanserv_saxdb_read(struct dict
*database
)
7695 struct dict
*section
;
7698 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7699 chanserv_version_read(section
);
7701 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7702 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7703 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7705 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7706 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7707 chanserv_channel_read(iter_key(it
), iter_data(it
));
7709 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7710 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7711 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7717 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7719 int high_present
= 0;
7720 saxdb_start_record(ctx
, KEY_USERS
, 1);
7721 for(; uData
; uData
= uData
->next
)
7723 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7725 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7726 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7727 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7729 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7731 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7732 saxdb_end_record(ctx
);
7734 saxdb_end_record(ctx
);
7735 return high_present
;
7739 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7743 saxdb_start_record(ctx
, KEY_BANS
, 1);
7744 for(; bData
; bData
= bData
->next
)
7746 saxdb_start_record(ctx
, bData
->mask
, 0);
7747 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7748 if(bData
->triggered
)
7749 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7751 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7753 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7755 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7756 saxdb_end_record(ctx
);
7758 saxdb_end_record(ctx
);
7762 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7764 saxdb_start_record(ctx
, name
, 0);
7765 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7766 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7768 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7770 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7772 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7774 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7775 saxdb_end_record(ctx
);
7779 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7783 enum levelOption lvlOpt
;
7784 enum charOption chOpt
;
7786 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7788 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7789 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7791 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7792 if(channel
->registrar
)
7793 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7794 if(channel
->greeting
)
7795 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7796 if(channel
->user_greeting
)
7797 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7798 if(channel
->topic_mask
)
7799 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7800 if(channel
->suspended
)
7801 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7803 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7804 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7805 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7806 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7807 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7809 buf
[0] = channel
->chOpts
[chOpt
];
7811 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7813 saxdb_end_record(ctx
);
7815 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7817 mod_chanmode_format(&channel
->modes
, buf
);
7818 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7821 high_present
= chanserv_write_users(ctx
, channel
->users
);
7822 chanserv_write_bans(ctx
, channel
->bans
);
7824 if(dict_size(channel
->notes
))
7828 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7829 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7831 struct note
*note
= iter_data(it
);
7832 saxdb_start_record(ctx
, iter_key(it
), 0);
7833 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7834 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7835 saxdb_end_record(ctx
);
7837 saxdb_end_record(ctx
);
7840 if(channel
->ownerTransfer
)
7841 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7842 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7843 saxdb_end_record(ctx
);
7847 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7851 saxdb_start_record(ctx
, ntype
->name
, 0);
7852 switch(ntype
->set_access_type
)
7854 case NOTE_SET_CHANNEL_ACCESS
:
7855 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7857 case NOTE_SET_CHANNEL_SETTER
:
7858 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7860 case NOTE_SET_PRIVILEGED
: default:
7861 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7864 switch(ntype
->visible_type
)
7866 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7867 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7868 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7870 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7871 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7872 saxdb_end_record(ctx
);
7876 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7878 struct do_not_register
*dnr
;
7881 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7883 dnr
= iter_data(it
);
7884 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7886 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7887 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7888 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7889 saxdb_end_record(ctx
);
7894 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7897 struct chanData
*channel
;
7899 /* Version Control*/
7900 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
7901 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
7902 saxdb_end_record(ctx
);
7905 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7906 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7907 chanserv_write_note_type(ctx
, iter_data(it
));
7908 saxdb_end_record(ctx
);
7911 saxdb_start_record(ctx
, KEY_DNR
, 1);
7912 write_dnrs_helper(ctx
, handle_dnrs
);
7913 write_dnrs_helper(ctx
, plain_dnrs
);
7914 write_dnrs_helper(ctx
, mask_dnrs
);
7915 saxdb_end_record(ctx
);
7918 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7919 for(channel
= channelList
; channel
; channel
= channel
->next
)
7920 chanserv_write_channel(ctx
, channel
);
7921 saxdb_end_record(ctx
);
7927 chanserv_db_cleanup(void) {
7929 unreg_part_func(handle_part
);
7931 unregister_channel(channelList
, "terminating.");
7932 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7933 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7934 free(chanserv_conf
.support_channels
.list
);
7935 dict_delete(handle_dnrs
);
7936 dict_delete(plain_dnrs
);
7937 dict_delete(mask_dnrs
);
7938 dict_delete(note_types
);
7939 free_string_list(chanserv_conf
.eightball
);
7940 free_string_list(chanserv_conf
.old_ban_names
);
7941 free_string_list(chanserv_conf
.set_shows
);
7942 free(set_shows_list
.list
);
7943 free(uset_shows_list
.list
);
7946 struct userData
*helper
= helperList
;
7947 helperList
= helperList
->next
;
7952 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7953 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7954 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7957 init_chanserv(const char *nick
)
7959 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7960 conf_register_reload(chanserv_conf_read
);
7962 reg_server_link_func(handle_server_link
);
7964 reg_new_channel_func(handle_new_channel
);
7965 reg_join_func(handle_join
);
7966 reg_part_func(handle_part
);
7967 reg_kick_func(handle_kick
);
7968 reg_topic_func(handle_topic
);
7969 reg_mode_change_func(handle_mode
);
7970 reg_nick_change_func(handle_nick_change
);
7972 reg_auth_func(handle_auth
);
7973 reg_handle_rename_func(handle_rename
);
7974 reg_unreg_func(handle_unreg
);
7976 handle_dnrs
= dict_new();
7977 dict_set_free_data(handle_dnrs
, free
);
7978 plain_dnrs
= dict_new();
7979 dict_set_free_data(plain_dnrs
, free
);
7980 mask_dnrs
= dict_new();
7981 dict_set_free_data(mask_dnrs
, free
);
7983 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7984 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7985 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7986 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7987 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7988 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7989 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7990 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7991 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7992 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7994 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7996 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7997 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7999 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8000 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8001 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8002 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8003 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8005 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8006 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8007 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8008 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8009 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8010 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8012 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8013 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8014 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8015 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8017 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8018 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8019 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8020 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8021 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8022 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8023 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8024 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8025 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8026 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8028 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8029 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8030 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8031 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8032 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8033 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8034 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8035 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8036 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8037 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8038 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8039 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8040 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8041 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8043 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8044 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8045 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8046 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8048 /* if you change dellamer access, see also places
8049 * like unbanme which have manager hardcoded. */
8050 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8051 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8053 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8055 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8057 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8058 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8059 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8060 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8061 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8062 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8063 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8064 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8065 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8066 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8067 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8068 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8070 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8071 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8073 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8074 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8075 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8076 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8078 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8079 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8080 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8081 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8082 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8084 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8085 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8086 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8087 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8088 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8089 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8090 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8092 /* Channel options */
8093 DEFINE_CHANNEL_OPTION(defaulttopic
);
8094 DEFINE_CHANNEL_OPTION(topicmask
);
8095 DEFINE_CHANNEL_OPTION(greeting
);
8096 DEFINE_CHANNEL_OPTION(usergreeting
);
8097 DEFINE_CHANNEL_OPTION(modes
);
8098 DEFINE_CHANNEL_OPTION(enfops
);
8099 DEFINE_CHANNEL_OPTION(enfhalfops
);
8100 /*DEFINE_CHANNEL_OPTION(giveops);
8101 DEFINE_CHANNEL_OPTION(givehalfops);
8103 DEFINE_CHANNEL_OPTION(voice
);
8104 DEFINE_CHANNEL_OPTION(protect
);
8105 DEFINE_CHANNEL_OPTION(enfmodes
);
8106 DEFINE_CHANNEL_OPTION(enftopic
);
8107 DEFINE_CHANNEL_OPTION(pubcmd
);
8108 /*DEFINE_CHANNEL_OPTION(givevoice);
8110 DEFINE_CHANNEL_OPTION(userinfo
);
8111 DEFINE_CHANNEL_OPTION(dynlimit
);
8112 DEFINE_CHANNEL_OPTION(topicsnarf
);
8113 DEFINE_CHANNEL_OPTION(nodelete
);
8114 DEFINE_CHANNEL_OPTION(toys
);
8115 DEFINE_CHANNEL_OPTION(setters
);
8116 DEFINE_CHANNEL_OPTION(topicrefresh
);
8117 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8118 DEFINE_CHANNEL_OPTION(bantimeout
);
8119 DEFINE_CHANNEL_OPTION(inviteme
);
8121 DEFINE_CHANNEL_OPTION(offchannel
);
8122 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8124 /* Alias set topic to set defaulttopic for compatibility. */
8125 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8128 DEFINE_USER_OPTION(autoinvite
);
8129 DEFINE_USER_OPTION(info
);
8130 DEFINE_USER_OPTION(autoop
);
8132 /* Alias uset autovoice to uset autoop. */
8133 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8135 note_types
= dict_new();
8136 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8139 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8140 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8141 service_register(chanserv
)->trigger
= '!';
8142 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8145 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8147 if(chanserv_conf
.channel_expire_frequency
)
8148 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8150 if(chanserv_conf
.ban_timeout_frequency
)
8151 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8153 if(chanserv_conf
.refresh_period
)
8155 time_t next_refresh
;
8156 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8157 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8160 reg_exit_func(chanserv_db_cleanup
);
8161 message_register_table(msgtab
);