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_GIVEOWNERSHIP "giveownership"
96 #define KEY_STAFF_ISSUER "staff_issuer"
97 #define KEY_OLD_OWNER "old_owner"
98 #define KEY_TARGET "target"
99 #define KEY_TARGET_ACCESS "target_access"
100 #define KEY_VISITED "visited"
101 #define KEY_TOPIC "topic"
102 #define KEY_GREETING "greeting"
103 #define KEY_USER_GREETING "user_greeting"
104 #define KEY_MODES "modes"
105 #define KEY_FLAGS "flags"
106 #define KEY_OPTIONS "options"
107 #define KEY_USERS "users"
108 #define KEY_BANS "bans" /* for lamers */
109 #define KEY_MAX "max"
110 #define KEY_NOTES "notes"
111 #define KEY_TOPIC_MASK "topic_mask"
112 #define KEY_OWNER_TRANSFER "owner_transfer"
115 #define KEY_LEVEL "level"
116 #define KEY_INFO "info"
117 #define KEY_SEEN "seen"
120 #define KEY_OWNER "owner"
121 #define KEY_REASON "reason"
122 #define KEY_SET "set"
123 #define KEY_DURATION "duration"
124 #define KEY_EXPIRES "expires"
125 #define KEY_TRIGGERED "triggered"
127 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
128 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
130 /* Administrative messages */
131 static const struct message_entry msgtab
[] = {
132 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
134 /* Channel registration */
135 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
136 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
137 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
138 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
139 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
140 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
141 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
142 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
144 /* Do-not-register channels */
145 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
146 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
147 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
148 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
149 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
150 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
151 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
152 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
153 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
154 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
155 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
157 /* Channel unregistration */
158 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
159 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
160 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
161 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
164 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
165 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
167 /* Channel merging */
168 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
169 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
170 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
171 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
172 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
174 /* Handle unregistration */
175 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
178 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
179 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
180 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
181 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
182 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
183 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
184 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
185 { "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." },
186 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
187 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
188 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
189 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
190 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
191 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
193 /* Removing yourself from a channel. */
194 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
195 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
196 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
198 /* User management */
199 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
200 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
201 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
202 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
203 { "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." },
204 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
205 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
206 { "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." },
207 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
208 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
209 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
210 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
211 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
212 { "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" },
213 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
215 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
216 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
217 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
218 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
219 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
220 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
223 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
224 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
225 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
226 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
227 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
228 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
229 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
230 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
231 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
232 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
233 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
234 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
235 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
236 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
237 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
238 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
239 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
241 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
243 /* Channel management */
244 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
245 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
246 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
248 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
249 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
250 { "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" },
251 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
252 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
253 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
254 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
256 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
257 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
258 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
259 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
260 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
261 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
262 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
263 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
264 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
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." },
277 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
278 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
279 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
280 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
281 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
282 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
283 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
284 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
285 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
286 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
287 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
288 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
289 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
290 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
292 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
293 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
294 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
295 { "CSMSG_USET_INFO", "$bInfo $b %s" },
297 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
298 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
299 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
300 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
301 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
302 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
303 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
304 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
305 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
306 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
307 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
309 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
310 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
311 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
312 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
313 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
314 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
316 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
317 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
318 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
319 { "CSMSG_PROTECT_NONE", "No users will be protected." },
320 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
321 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
322 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
324 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
325 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
326 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
327 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
328 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
330 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
331 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
332 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
333 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
334 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
336 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
337 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
338 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
339 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
340 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
341 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
343 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
344 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
345 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
346 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
347 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
348 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
349 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
350 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
352 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
353 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
354 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
356 /* Channel userlist */
357 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
358 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
359 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
360 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
361 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
363 /* Channel note list */
364 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
365 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
366 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
367 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
368 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
369 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
370 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
371 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
372 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
373 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
374 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
375 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
376 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
377 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
378 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
380 /* Channel [un]suspension */
381 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
382 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
383 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
384 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
385 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
386 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
387 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
389 /* Access information */
390 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
391 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
392 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
393 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
394 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
395 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
396 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
397 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
398 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
399 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
400 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
402 /* Seen information */
403 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
404 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
405 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
406 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
408 /* Names information */
409 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
410 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
412 /* Channel information */
413 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
414 { "CSMSG_BAR", "----------------------------------------"},
415 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
416 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
417 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
418 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
419 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
420 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
421 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
422 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
423 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
424 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
425 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
426 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
427 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
428 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
429 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
430 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
431 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
433 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
434 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
435 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
436 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
437 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
438 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
439 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
441 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
442 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
443 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
444 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
445 { "CSMSG_PEEK_OPS", "$bOps:$b" },
446 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
447 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
449 /* Network information */
450 { "CSMSG_NETWORK_INFO", "Network Information:" },
451 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
452 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
453 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
454 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
455 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
456 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
457 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
458 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
461 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
462 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
463 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
465 /* Channel searches */
466 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
467 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
468 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
469 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
471 /* Channel configuration */
472 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
473 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
474 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
475 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
478 { "CSMSG_USER_OPTIONS", "User Options:" },
479 // { "CSMSG_USER_PROTECTED", "That user is protected." },
482 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
483 { "CSMSG_PING_RESPONSE", "Pong!" },
484 { "CSMSG_WUT_RESPONSE", "wut" },
485 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
486 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
487 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
488 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
489 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
490 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
491 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
494 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
495 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
499 /* eject_user and unban_user flags */
500 #define ACTION_KICK 0x0001
501 #define ACTION_BAN 0x0002
502 #define ACTION_ADD_LAMER 0x0004
503 #define ACTION_ADD_TIMED_LAMER 0x0008
504 #define ACTION_UNBAN 0x0010
505 #define ACTION_DEL_LAMER 0x0020
507 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
508 #define MODELEN 40 + KEYLEN
512 #define CSFUNC_ARGS user, channel, argc, argv, cmd
514 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
515 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
516 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
517 reply("MSG_MISSING_PARAMS", argv[0]); \
521 DECLARE_LIST(dnrList
, struct do_not_register
*);
522 DEFINE_LIST(dnrList
, struct do_not_register
*);
524 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
526 struct userNode
*chanserv
;
529 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
530 static struct log_type
*CS_LOG
;
531 struct adduserPending
* adduser_pendings
= NULL
;
532 unsigned int adduser_pendings_count
= 0;
536 struct channelList support_channels
;
537 struct mod_chanmode default_modes
;
539 unsigned long db_backup_frequency
;
540 unsigned long channel_expire_frequency
;
541 unsigned long ban_timeout_frequency
;
544 unsigned int adjust_delay
;
545 long channel_expire_delay
;
546 unsigned int nodelete_level
;
548 unsigned int adjust_threshold
;
549 int join_flood_threshold
;
551 unsigned int greeting_length
;
552 unsigned int refresh_period
;
553 unsigned int giveownership_period
;
555 unsigned int max_owned
;
556 unsigned int max_chan_users
;
557 unsigned int max_chan_bans
; /* lamers */
558 unsigned int max_userinfo_length
;
560 struct string_list
*set_shows
;
561 struct string_list
*eightball
;
562 struct string_list
*old_ban_names
;
564 const char *ctcp_short_ban_duration
;
565 const char *ctcp_long_ban_duration
;
567 const char *irc_operator_epithet
;
568 const char *network_helper_epithet
;
569 const char *support_helper_epithet
;
574 struct userNode
*user
;
575 struct userNode
*bot
;
576 struct chanNode
*channel
;
578 unsigned short lowest
;
579 unsigned short highest
;
580 struct userData
**users
;
581 struct helpfile_table table
;
584 enum note_access_type
586 NOTE_SET_CHANNEL_ACCESS
,
587 NOTE_SET_CHANNEL_SETTER
,
591 enum note_visible_type
594 NOTE_VIS_CHANNEL_USERS
,
600 enum note_access_type set_access_type
;
602 unsigned int min_opserv
;
603 unsigned short min_ulevel
;
605 enum note_visible_type visible_type
;
606 unsigned int max_length
;
613 struct note_type
*type
;
614 char setter
[NICKSERV_HANDLE_LEN
+1];
618 static unsigned int registered_channels
;
619 static unsigned int banCount
;
621 static const struct {
624 unsigned short level
;
626 } accessLevels
[] = { /* MUST be orderd less to most! */
627 { "peon", "Peon", UL_PEON
, '+' },
628 { "halfop", "HalfOp", UL_HALFOP
, '%' },
629 { "op", "Op", UL_OP
, '@' },
630 { "manager", "Manager", UL_MANAGER
, '%' },
631 { "coowner", "Coowner", UL_COOWNER
, '*' },
632 { "owner", "Owner", UL_OWNER
, '!' },
633 { "helper", "BUG:", UL_HELPER
, 'X' }
636 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
637 static const struct {
640 unsigned short default_value
;
641 unsigned int old_idx
;
642 unsigned int old_flag
;
643 unsigned short flag_value
;
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_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
652 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
653 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
656 struct charOptionValues
{
659 } automodeValues
[] = {
660 { 'n', "CSMSG_AUTOMODE_NONE" },
661 { 'y', "CSMSG_AUTOMODE_NORMAL" },
662 { 'v', "CSMSG_AUTOMODE_VOICE" },
663 { 'h', "CSMSG_AUTOMODE_HOP" },
664 { 'o', "CSMSG_AUTOMODE_OP" },
665 { 'm', "CSMSG_AUTOMODE_MUTE" }
666 }, protectValues
[] = {
667 { 'a', "CSMSG_PROTECT_ALL" },
668 { 'e', "CSMSG_PROTECT_EQUAL" },
669 { 'l', "CSMSG_PROTECT_LOWER" },
670 { 'n', "CSMSG_PROTECT_NONE" }
672 { 'd', "CSMSG_TOYS_DISABLED" },
673 { 'n', "CSMSG_TOYS_PRIVATE" },
674 { 'p', "CSMSG_TOYS_PUBLIC" }
675 }, topicRefreshValues
[] = {
676 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
677 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
678 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
679 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
680 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
681 }, ctcpReactionValues
[] = {
682 { 'n', "CSMSG_CTCPREACTION_NONE" },
683 { 'k', "CSMSG_CTCPREACTION_KICK" },
684 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
685 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
686 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
687 }, banTimeoutValues
[] = {
688 { '0', "CSMSG_BANTIMEOUT_NONE" },
689 { '1', "CSMSG_BANTIMEOUT_10M" },
690 { '2', "CSMSG_BANTIMEOUT_2H" },
691 { '3', "CSMSG_BANTIMEOUT_4H" },
692 { '4', "CSMSG_BANTIMEOUT_1D" },
693 { '5', "CSMSG_BANTIMEOUT_1W" }
696 static const struct {
700 unsigned int old_idx
;
702 struct charOptionValues
*values
;
704 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
705 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
706 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
707 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
708 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
709 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
}
712 struct userData
*helperList
;
713 struct chanData
*channelList
;
714 static struct module *chanserv_module
;
715 static unsigned int userCount
;
716 unsigned int chanserv_read_version
= 0; /* db version control */
718 #define CHANSERV_DB_VERSION 2
720 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
721 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
722 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
725 user_level_from_name(const char *name
, unsigned short clamp_level
)
727 unsigned int level
= 0, ii
;
729 level
= strtoul(name
, NULL
, 10);
730 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
731 if(!irccasecmp(name
, accessLevels
[ii
].name
))
732 level
= accessLevels
[ii
].level
;
733 if(level
> clamp_level
)
739 user_level_name_from_level(int level
)
747 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
748 if(level
>= accessLevels
[ii
].level
)
749 highest
= accessLevels
[ii
].title
;
755 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
758 *minl
= strtoul(arg
, &sep
, 10);
766 *maxl
= strtoul(sep
+1, &sep
, 10);
774 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
776 struct userData
*uData
, **head
;
778 if(!channel
|| !handle
)
781 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
782 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
784 for(uData
= helperList
;
785 uData
&& uData
->handle
!= handle
;
786 uData
= uData
->next
);
790 uData
= calloc(1, sizeof(struct userData
));
791 uData
->handle
= handle
;
793 uData
->access
= UL_HELPER
;
799 uData
->next
= helperList
;
801 helperList
->prev
= uData
;
809 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
810 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
813 head
= &(channel
->users
);
816 if(uData
&& (uData
!= *head
))
818 /* Shuffle the user to the head of whatever list he was in. */
820 uData
->next
->prev
= uData
->prev
;
822 uData
->prev
->next
= uData
->next
;
828 (**head
).prev
= uData
;
835 /* Returns non-zero if user has at least the minimum access.
836 * exempt_owner is set when handling !set, so the owner can set things
839 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
841 struct userData
*uData
;
842 struct chanData
*cData
= channel
->channel_info
;
843 unsigned short minimum
= cData
->lvlOpts
[opt
];
846 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
849 if(minimum
<= uData
->access
)
851 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
856 /* Scan for other users authenticated to the same handle
857 still in the channel. If so, keep them listed as present.
859 user is optional, if not null, it skips checking that userNode
860 (for the handle_part function) */
862 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
866 if(IsSuspended(uData
->channel
)
867 || IsUserSuspended(uData
)
868 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
880 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
882 unsigned int eflags
, argc
;
884 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
886 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
887 if(!channel
->channel_info
888 || IsSuspended(channel
->channel_info
)
890 || !ircncasecmp(text
, "ACTION ", 7))
892 /* We dont punish people we know -Rubin
893 * * Figure out the minimum level needed to CTCP the channel *
895 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
898 /* If they are a user of the channel, they are exempt */
899 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
901 /* We need to enforce against them; do so. */
904 argv
[1] = user
->nick
;
906 if(GetUserMode(channel
, user
))
907 eflags
|= ACTION_KICK
;
908 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
909 default: case 'n': return;
911 eflags
|= ACTION_KICK
;
914 eflags
|= ACTION_BAN
;
917 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
918 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
921 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
922 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
925 argv
[argc
++] = bad_ctcp_reason
;
926 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
930 chanserv_create_note_type(const char *name
)
932 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
933 strcpy(ntype
->name
, name
);
935 dict_insert(note_types
, ntype
->name
, ntype
);
940 chanserv_deref_note_type(void *data
)
942 struct note_type
*ntype
= data
;
944 if(--ntype
->refs
> 0)
950 chanserv_flush_note_type(struct note_type
*ntype
)
952 struct chanData
*cData
;
953 for(cData
= channelList
; cData
; cData
= cData
->next
)
954 dict_remove(cData
->notes
, ntype
->name
);
958 chanserv_truncate_notes(struct note_type
*ntype
)
960 struct chanData
*cData
;
962 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
964 for(cData
= channelList
; cData
; cData
= cData
->next
) {
965 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
968 if(strlen(note
->note
) <= ntype
->max_length
)
970 dict_remove2(cData
->notes
, ntype
->name
, 1);
971 note
= realloc(note
, size
);
972 note
->note
[ntype
->max_length
] = 0;
973 dict_insert(cData
->notes
, ntype
->name
, note
);
977 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
980 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
983 unsigned int len
= strlen(text
);
985 if(len
> type
->max_length
) len
= type
->max_length
;
986 note
= calloc(1, sizeof(*note
) + len
);
988 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
989 memcpy(note
->note
, text
, len
);
991 dict_insert(channel
->notes
, type
->name
, note
);
997 chanserv_free_note(void *data
)
999 struct note
*note
= data
;
1001 chanserv_deref_note_type(note
->type
);
1002 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1006 static MODCMD_FUNC(cmd_createnote
) {
1007 struct note_type
*ntype
;
1008 unsigned int arg
= 1, existed
= 0, max_length
;
1010 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1013 ntype
= chanserv_create_note_type(argv
[arg
]);
1014 if(!irccasecmp(argv
[++arg
], "privileged"))
1017 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1018 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1020 else if(!irccasecmp(argv
[arg
], "channel"))
1022 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1025 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1028 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1029 ntype
->set_access
.min_ulevel
= ulvl
;
1031 else if(!irccasecmp(argv
[arg
], "setter"))
1033 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1037 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1041 if(!irccasecmp(argv
[++arg
], "privileged"))
1042 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1043 else if(!irccasecmp(argv
[arg
], "channel_users"))
1044 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1045 else if(!irccasecmp(argv
[arg
], "all"))
1046 ntype
->visible_type
= NOTE_VIS_ALL
;
1048 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1052 if((arg
+1) >= argc
) {
1053 reply("MSG_MISSING_PARAMS", argv
[0]);
1056 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1057 if(max_length
< 20 || max_length
> 450)
1059 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1062 if(existed
&& (max_length
< ntype
->max_length
))
1064 ntype
->max_length
= max_length
;
1065 chanserv_truncate_notes(ntype
);
1067 ntype
->max_length
= max_length
;
1070 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1072 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1077 dict_remove(note_types
, ntype
->name
);
1081 static MODCMD_FUNC(cmd_removenote
) {
1082 struct note_type
*ntype
;
1085 ntype
= dict_find(note_types
, argv
[1], NULL
);
1086 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1089 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1096 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1099 chanserv_flush_note_type(ntype
);
1101 dict_remove(note_types
, argv
[1]);
1102 reply("CSMSG_NOTE_DELETED", argv
[1]);
1107 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1111 if(orig
->modes_set
& change
->modes_clear
)
1113 if(orig
->modes_clear
& change
->modes_set
)
1115 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1116 && strcmp(orig
->new_key
, change
->new_key
))
1118 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1119 && (orig
->new_limit
!= change
->new_limit
))
1124 static char max_length_text
[MAXLEN
+1][16];
1126 static struct helpfile_expansion
1127 chanserv_expand_variable(const char *variable
)
1129 struct helpfile_expansion exp
;
1131 if(!irccasecmp(variable
, "notes"))
1134 exp
.type
= HF_TABLE
;
1135 exp
.value
.table
.length
= 1;
1136 exp
.value
.table
.width
= 3;
1137 exp
.value
.table
.flags
= 0;
1138 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1139 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1140 exp
.value
.table
.contents
[0][0] = "Note Type";
1141 exp
.value
.table
.contents
[0][1] = "Visibility";
1142 exp
.value
.table
.contents
[0][2] = "Max Length";
1143 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1145 struct note_type
*ntype
= iter_data(it
);
1148 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1149 row
= exp
.value
.table
.length
++;
1150 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1151 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1152 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1153 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1155 if(!max_length_text
[ntype
->max_length
][0])
1156 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1157 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1162 exp
.type
= HF_STRING
;
1163 exp
.value
.str
= NULL
;
1167 static struct chanData
*
1168 register_channel(struct chanNode
*cNode
, char *registrar
)
1170 struct chanData
*channel
;
1171 enum levelOption lvlOpt
;
1172 enum charOption chOpt
;
1174 channel
= calloc(1, sizeof(struct chanData
));
1176 channel
->notes
= dict_new();
1177 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1179 channel
->registrar
= strdup(registrar
);
1180 channel
->registered
= now
;
1181 channel
->visited
= now
;
1182 channel
->limitAdjusted
= now
;
1183 channel
->ownerTransfer
= now
;
1184 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1185 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1186 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1187 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1188 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1190 channel
->prev
= NULL
;
1191 channel
->next
= channelList
;
1194 channelList
->prev
= channel
;
1195 channelList
= channel
;
1196 registered_channels
++;
1198 channel
->channel
= cNode
;
1200 cNode
->channel_info
= channel
;
1205 static struct userData
*
1206 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1208 struct userData
*ud
;
1210 if(access
> UL_OWNER
)
1213 ud
= calloc(1, sizeof(*ud
));
1214 ud
->channel
= channel
;
1215 ud
->handle
= handle
;
1217 ud
->access
= access
;
1218 ud
->info
= info
? strdup(info
) : NULL
;
1221 ud
->next
= channel
->users
;
1223 channel
->users
->prev
= ud
;
1224 channel
->users
= ud
;
1226 channel
->userCount
++;
1230 ud
->u_next
= ud
->handle
->channels
;
1232 ud
->u_next
->u_prev
= ud
;
1233 ud
->handle
->channels
= ud
;
1235 ud
->flags
= USER_FLAGS_DEFAULT
;
1239 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1242 del_channel_user(struct userData
*user
, int do_gc
)
1244 struct chanData
*channel
= user
->channel
;
1246 channel
->userCount
--;
1250 user
->prev
->next
= user
->next
;
1252 channel
->users
= user
->next
;
1254 user
->next
->prev
= user
->prev
;
1257 user
->u_prev
->u_next
= user
->u_next
;
1259 user
->handle
->channels
= user
->u_next
;
1261 user
->u_next
->u_prev
= user
->u_prev
;
1265 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1266 unregister_channel(channel
, "lost all users.");
1269 static struct adduserPending
*
1270 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1272 struct adduserPending
*ap
;
1273 ap
= calloc(1,sizeof(struct adduserPending
));
1274 ap
->channel
= channel
;
1277 ap
->created
= time(NULL
);
1279 /* ap->prev defaults to NULL already.. */
1280 ap
->next
= adduser_pendings
;
1281 if(adduser_pendings
)
1282 adduser_pendings
->prev
= ap
;
1283 adduser_pendings
= ap
;
1284 adduser_pendings_count
++;
1289 del_adduser_pending(struct adduserPending
*ap
)
1292 ap
->prev
->next
= ap
->next
;
1294 adduser_pendings
= ap
->next
;
1297 ap
->next
->prev
= ap
->prev
;
1301 static void expire_adduser_pending();
1303 /* find_adduser_pending(channel, user) will find an arbitrary record
1304 * from user, channel, or user and channel.
1305 * if user or channel are NULL, they will match any records.
1307 static struct adduserPending
*
1308 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1310 struct adduserPending
*ap
;
1312 expire_adduser_pending(); /* why not here.. */
1314 if(!channel
&& !user
) /* 2 nulls matches all */
1315 return(adduser_pendings
);
1316 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1318 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1325 /* Remove all pendings for a user or channel
1327 * called in nickserv.c DelUser() and proto-* unregister_channel()
1330 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1332 struct adduserPending
*ap
;
1334 /* So this is a bit wastefull, i hate dealing with linked lists.
1335 * if its a problem we'll rewrite it right */
1336 while((ap
= find_adduser_pending(channel
, user
))) {
1337 del_adduser_pending(ap
);
1341 /* Called from nickserv.c cmd_auth after someone auths */
1343 process_adduser_pending(struct userNode
*user
)
1345 struct adduserPending
*ap
;
1346 if(!user
->handle_info
)
1347 return; /* not associated with an account */
1348 while((ap
= find_adduser_pending(NULL
, user
)))
1350 struct userData
*actee
;
1351 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1353 /* Already on the userlist. do nothing*/
1357 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1358 scan_user_presence(actee
, NULL
);
1360 del_adduser_pending(ap
);
1365 expire_adduser_pending()
1367 struct adduserPending
*ap
, *ap_next
;
1368 ap
= adduser_pendings
;
1371 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1373 ap_next
= ap
->next
; /* save next */
1374 del_adduser_pending(ap
); /* free and relink */
1375 ap
= ap_next
; /* advance */
1382 static void expire_ban(void *data
);
1384 static struct banData
*
1385 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1388 unsigned int ii
, l1
, l2
;
1393 bd
= malloc(sizeof(struct banData
));
1395 bd
->channel
= channel
;
1397 bd
->triggered
= triggered
;
1398 bd
->expires
= expires
;
1400 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1402 extern const char *hidden_host_suffix
;
1403 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1407 l2
= strlen(old_name
);
1410 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1412 new_mask
= alloca(MAXLEN
);
1413 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1416 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1418 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1419 bd
->reason
= strdup(reason
);
1422 timeq_add(expires
, expire_ban
, bd
);
1425 bd
->next
= channel
->bans
; /* lamers */
1427 channel
->bans
->prev
= bd
;
1429 channel
->banCount
++;
1436 del_channel_ban(struct banData
*ban
)
1438 ban
->channel
->banCount
--;
1442 ban
->prev
->next
= ban
->next
;
1444 ban
->channel
->bans
= ban
->next
;
1447 ban
->next
->prev
= ban
->prev
;
1450 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1459 expire_ban(void *data
) /* lamer.. */
1461 struct banData
*bd
= data
;
1462 if(!IsSuspended(bd
->channel
))
1464 struct banList bans
;
1465 struct mod_chanmode change
;
1467 bans
= bd
->channel
->channel
->banlist
;
1468 mod_chanmode_init(&change
);
1469 for(ii
=0; ii
<bans
.used
; ii
++)
1471 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1474 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1475 change
.args
[0].u
.hostmask
= bd
->mask
;
1476 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1482 del_channel_ban(bd
);
1485 static void chanserv_expire_suspension(void *data
);
1488 unregister_channel(struct chanData
*channel
, const char *reason
)
1490 struct mod_chanmode change
;
1491 char msgbuf
[MAXLEN
];
1493 /* After channel unregistration, the following must be cleaned
1495 - Channel information.
1497 - Channel bans. (lamers)
1498 - Channel suspension data.
1499 - adduser_pending data.
1500 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1506 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1510 mod_chanmode_init(&change
);
1511 change
.modes_clear
|= MODE_REGISTERED
;
1512 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1515 wipe_adduser_pending(channel
->channel
, NULL
);
1517 while(channel
->users
)
1518 del_channel_user(channel
->users
, 0);
1520 while(channel
->bans
)
1521 del_channel_ban(channel
->bans
);
1523 free(channel
->topic
);
1524 free(channel
->registrar
);
1525 free(channel
->greeting
);
1526 free(channel
->user_greeting
);
1527 free(channel
->topic_mask
);
1530 channel
->prev
->next
= channel
->next
;
1532 channelList
= channel
->next
;
1535 channel
->next
->prev
= channel
->prev
;
1537 if(channel
->suspended
)
1539 struct chanNode
*cNode
= channel
->channel
;
1540 struct suspended
*suspended
, *next_suspended
;
1542 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1544 next_suspended
= suspended
->previous
;
1545 free(suspended
->suspender
);
1546 free(suspended
->reason
);
1547 if(suspended
->expires
)
1548 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1553 cNode
->channel_info
= NULL
;
1555 channel
->channel
->channel_info
= NULL
;
1557 dict_delete(channel
->notes
);
1558 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1559 if(!IsSuspended(channel
))
1560 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1561 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1562 UnlockChannel(channel
->channel
);
1564 registered_channels
--;
1568 expire_channels(UNUSED_ARG(void *data
))
1570 struct chanData
*channel
, *next
;
1571 struct userData
*user
;
1572 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1574 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1575 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1577 for(channel
= channelList
; channel
; channel
= next
)
1579 next
= channel
->next
;
1581 /* See if the channel can be expired. */
1582 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1583 || IsProtected(channel
))
1586 /* Make sure there are no high-ranking users still in the channel. */
1587 for(user
=channel
->users
; user
; user
=user
->next
)
1588 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1593 /* Unregister the channel */
1594 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1595 unregister_channel(channel
, "registration expired.");
1598 if(chanserv_conf
.channel_expire_frequency
)
1599 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1603 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1605 char protect
= channel
->chOpts
[chProtect
];
1606 struct userData
*cs_victim
, *cs_aggressor
;
1608 /* Don't protect if no one is to be protected, someone is attacking
1609 himself, or if the aggressor is an IRC Operator. */
1610 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1613 /* Don't protect if the victim isn't authenticated (because they
1614 can't be a channel user), unless we are to protect non-users
1616 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1617 if(protect
!= 'a' && !cs_victim
)
1620 /* Protect if the aggressor isn't a user because at this point,
1621 the aggressor can only be less than or equal to the victim. */
1622 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1626 /* If the aggressor was a user, then the victim can't be helped. */
1633 if(cs_victim
->access
> cs_aggressor
->access
)
1638 if(cs_victim
->access
>= cs_aggressor
->access
)
1647 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1649 struct chanData
*cData
= channel
->channel_info
;
1650 struct userData
*cs_victim
;
1652 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1653 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1654 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1656 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1664 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1666 struct chanData
*cData
= channel
->channel_info
;
1667 struct userData
*cs_victim
;
1669 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1670 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1671 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1673 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1682 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1684 if(IsService(victim
))
1686 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1690 if(protect_user(victim
, user
, channel
->channel_info
))
1692 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1700 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1702 if(IsService(victim
))
1704 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1708 if(protect_user(victim
, user
, channel
->channel_info
))
1710 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1717 static struct do_not_register
*
1718 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1720 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1721 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1722 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1723 strcpy(dnr
->reason
, reason
);
1725 if(dnr
->chan_name
[0] == '*')
1726 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1727 else if(strpbrk(dnr
->chan_name
, "*?"))
1728 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1730 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1734 static struct dnrList
1735 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1737 struct dnrList list
;
1739 struct do_not_register
*dnr
;
1741 dnrList_init(&list
);
1742 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1743 dnrList_append(&list
, dnr
);
1744 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1745 dnrList_append(&list
, dnr
);
1747 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1748 if(match_ircglob(chan_name
, iter_key(it
)))
1749 dnrList_append(&list
, iter_data(it
));
1754 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1756 struct dnrList list
;
1757 struct do_not_register
*dnr
;
1759 char buf
[INTERVALLEN
];
1761 list
= chanserv_find_dnrs(chan_name
, handle
);
1762 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1764 dnr
= list
.list
[ii
];
1767 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1768 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1771 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1774 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1779 struct do_not_register
*
1780 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1782 struct do_not_register
*dnr
;
1785 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1789 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1791 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1792 if(match_ircglob(chan_name
, iter_key(it
)))
1793 return iter_data(it
);
1798 static CHANSERV_FUNC(cmd_noregister
)
1801 struct do_not_register
*dnr
;
1802 char buf
[INTERVALLEN
];
1803 unsigned int matches
;
1809 reply("CSMSG_DNR_SEARCH_RESULTS");
1812 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1814 dnr
= iter_data(it
);
1816 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1818 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1821 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1823 dnr
= iter_data(it
);
1825 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1827 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1830 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1832 dnr
= iter_data(it
);
1834 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1836 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1841 reply("MSG_MATCH_COUNT", matches
);
1843 reply("MSG_NO_MATCHES");
1849 if(!IsChannelName(target
) && (*target
!= '*'))
1851 reply("CSMSG_NOT_DNR", target
);
1857 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1858 if((*target
== '*') && !get_handle_info(target
+ 1))
1860 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1863 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1864 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1868 reply("CSMSG_DNR_SEARCH_RESULTS");
1871 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1873 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1875 reply("MSG_NO_MATCHES");
1879 static CHANSERV_FUNC(cmd_allowregister
)
1881 const char *chan_name
= argv
[1];
1883 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1885 dict_remove(handle_dnrs
, chan_name
+1);
1886 reply("CSMSG_DNR_REMOVED", chan_name
);
1888 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1890 dict_remove(plain_dnrs
, chan_name
);
1891 reply("CSMSG_DNR_REMOVED", chan_name
);
1893 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1895 dict_remove(mask_dnrs
, chan_name
);
1896 reply("CSMSG_DNR_REMOVED", chan_name
);
1900 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1907 chanserv_get_owned_count(struct handle_info
*hi
)
1909 struct userData
*cList
;
1912 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1913 if(cList
->access
== UL_OWNER
)
1918 static CHANSERV_FUNC(cmd_register
)
1920 struct handle_info
*handle
;
1921 struct chanData
*cData
;
1922 struct modeNode
*mn
;
1923 char reason
[MAXLEN
];
1925 unsigned int new_channel
, force
=0;
1926 struct do_not_register
*dnr
;
1932 if(channel
->channel_info
)
1934 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1938 if(channel
->bad_channel
)
1940 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1944 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1946 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1951 chan_name
= channel
->name
;
1957 reply("MSG_MISSING_PARAMS", cmd
->name
);
1958 svccmd_send_help_brief(user
, chanserv
, cmd
);
1961 if(!IsChannelName(argv
[1]))
1963 reply("MSG_NOT_CHANNEL_NAME");
1967 if(opserv_bad_channel(argv
[1]))
1969 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1974 chan_name
= argv
[1];
1977 if(argc
>= (new_channel
+2))
1979 if(!IsHelping(user
))
1981 reply("CSMSG_PROXY_FORBIDDEN");
1985 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1987 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1988 dnr
= chanserv_is_dnr(chan_name
, handle
);
1990 /* Check if they are over the limit.. */
1991 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1993 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2000 handle
= user
->handle_info
;
2001 dnr
= chanserv_is_dnr(chan_name
, handle
);
2002 /* Check if they are over the limit.. */
2003 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2005 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2008 /* Check if another service is in the channel */
2010 for(n
= 0; n
< channel
->members
.used
; n
++)
2012 mn
= channel
->members
.list
[n
];
2013 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2015 reply("CSMSG_ANOTHER_SERVICE");
2022 if(!IsHelping(user
))
2023 reply("CSMSG_DNR_CHANNEL", chan_name
);
2025 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2029 /* now handled above for message specilization *
2030 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2032 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2038 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2040 cData
= register_channel(channel
, user
->handle_info
->handle
);
2041 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2042 cData
->modes
= chanserv_conf
.default_modes
;
2044 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2045 if (IsOffChannel(cData
))
2047 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2051 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2052 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2053 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2055 mod_chanmode_announce(chanserv
, channel
, change
);
2056 mod_chanmode_free(change
);
2059 /* Initialize the channel's max user record. */
2060 cData
->max
= channel
->members
.used
;
2062 if(handle
!= user
->handle_info
)
2063 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2065 reply("CSMSG_REG_SUCCESS", channel
->name
);
2067 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2068 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2073 make_confirmation_string(struct userData
*uData
)
2075 static char strbuf
[16];
2080 for(src
= uData
->handle
->handle
; *src
; )
2081 accum
= accum
* 31 + toupper(*src
++);
2083 for(src
= uData
->channel
->channel
->name
; *src
; )
2084 accum
= accum
* 31 + toupper(*src
++);
2085 sprintf(strbuf
, "%08x", accum
);
2089 static CHANSERV_FUNC(cmd_unregister
)
2092 char reason
[MAXLEN
];
2093 struct chanData
*cData
;
2094 struct userData
*uData
;
2096 cData
= channel
->channel_info
;
2099 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2103 uData
= GetChannelUser(cData
, user
->handle_info
);
2104 if(!uData
|| (uData
->access
< UL_OWNER
))
2106 reply("CSMSG_NO_ACCESS");
2110 if(IsProtected(cData
))
2112 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2116 if(!IsHelping(user
))
2118 const char *confirm_string
;
2119 if(IsSuspended(cData
))
2121 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2124 confirm_string
= make_confirmation_string(uData
);
2125 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2127 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2132 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2133 name
= strdup(channel
->name
);
2134 unregister_channel(cData
, reason
);
2135 reply("CSMSG_UNREG_SUCCESS", name
);
2140 static CHANSERV_FUNC(cmd_move
)
2142 struct mod_chanmode change
;
2143 struct chanNode
*target
;
2144 struct modeNode
*mn
;
2145 struct userData
*uData
;
2146 char reason
[MAXLEN
];
2147 struct do_not_register
*dnr
;
2151 if(IsProtected(channel
->channel_info
))
2153 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2157 if(!IsChannelName(argv
[1]))
2159 reply("MSG_NOT_CHANNEL_NAME");
2163 if(opserv_bad_channel(argv
[1]))
2165 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2169 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2171 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2173 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2175 if(!IsHelping(user
))
2176 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2178 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2184 mod_chanmode_init(&change
);
2185 if(!(target
= GetChannel(argv
[1])))
2187 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2188 if(!IsSuspended(channel
->channel_info
))
2189 AddChannelUser(chanserv
, target
);
2191 else if(target
->channel_info
)
2193 reply("CSMSG_ALREADY_REGGED", target
->name
);
2196 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2197 && !IsHelping(user
))
2199 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2202 else if(!IsSuspended(channel
->channel_info
))
2205 change
.args
[0].mode
= MODE_CHANOP
;
2206 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2207 mod_chanmode_announce(chanserv
, target
, &change
);
2212 /* Clear MODE_REGISTERED from old channel, add it to new. */
2214 change
.modes_clear
= MODE_REGISTERED
;
2215 mod_chanmode_announce(chanserv
, channel
, &change
);
2216 change
.modes_clear
= 0;
2217 change
.modes_set
= MODE_REGISTERED
;
2218 mod_chanmode_announce(chanserv
, target
, &change
);
2221 /* Move the channel_info to the target channel; it
2222 shouldn't be necessary to clear timeq callbacks
2223 for the old channel. */
2224 target
->channel_info
= channel
->channel_info
;
2225 target
->channel_info
->channel
= target
;
2226 channel
->channel_info
= NULL
;
2228 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2230 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2231 if(!IsSuspended(target
->channel_info
))
2233 char reason2
[MAXLEN
];
2234 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2235 DelChannelUser(chanserv
, channel
, reason2
, 0);
2237 UnlockChannel(channel
);
2238 LockChannel(target
);
2239 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2244 merge_users(struct chanData
*source
, struct chanData
*target
)
2246 struct userData
*suData
, *tuData
, *next
;
2252 /* Insert the source's users into the scratch area. */
2253 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2254 dict_insert(merge
, suData
->handle
->handle
, suData
);
2256 /* Iterate through the target's users, looking for
2257 users common to both channels. The lower access is
2258 removed from either the scratch area or target user
2260 for(tuData
= target
->users
; tuData
; tuData
= next
)
2262 struct userData
*choice
;
2264 next
= tuData
->next
;
2266 /* If a source user exists with the same handle as a target
2267 channel's user, resolve the conflict by removing one. */
2268 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2272 /* Pick the data we want to keep. */
2273 /* If the access is the same, use the later seen time. */
2274 if(suData
->access
== tuData
->access
)
2275 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2276 else /* Otherwise, keep the higher access level. */
2277 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2279 /* Remove the user that wasn't picked. */
2280 if(choice
== tuData
)
2282 dict_remove(merge
, suData
->handle
->handle
);
2283 del_channel_user(suData
, 0);
2286 del_channel_user(tuData
, 0);
2289 /* Move the remaining users to the target channel. */
2290 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2292 suData
= iter_data(it
);
2294 /* Insert the user into the target channel's linked list. */
2295 suData
->prev
= NULL
;
2296 suData
->next
= target
->users
;
2297 suData
->channel
= target
;
2300 target
->users
->prev
= suData
;
2301 target
->users
= suData
;
2303 /* Update the user counts for the target channel; the
2304 source counts are left alone. */
2305 target
->userCount
++;
2308 /* Possible to assert (source->users == NULL) here. */
2309 source
->users
= NULL
;
2314 merge_bans(struct chanData
*source
, struct chanData
*target
)
2316 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2318 /* Hold on to the original head of the target ban list
2319 to avoid comparing source bans with source bans. */
2320 tFront
= target
->bans
;
2322 /* Perform a totally expensive O(n*m) merge, ick. */
2323 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2325 /* Flag to track whether the ban's been moved
2326 to the destination yet. */
2329 /* Possible to assert (sbData->prev == NULL) here. */
2330 sNext
= sbData
->next
;
2332 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2334 tNext
= tbData
->next
;
2336 /* Perform two comparisons between each source
2337 and target ban, conflicts are resolved by
2338 keeping the broader ban and copying the later
2339 expiration and triggered time. */
2340 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2342 /* There is a broader ban in the target channel that
2343 overrides one in the source channel; remove the
2344 source ban and break. */
2345 if(sbData
->expires
> tbData
->expires
)
2346 tbData
->expires
= sbData
->expires
;
2347 if(sbData
->triggered
> tbData
->triggered
)
2348 tbData
->triggered
= sbData
->triggered
;
2349 del_channel_ban(sbData
);
2352 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2354 /* There is a broader ban in the source channel that
2355 overrides one in the target channel; remove the
2356 target ban, fall through and move the source over. */
2357 if(tbData
->expires
> sbData
->expires
)
2358 sbData
->expires
= tbData
->expires
;
2359 if(tbData
->triggered
> sbData
->triggered
)
2360 sbData
->triggered
= tbData
->triggered
;
2361 if(tbData
== tFront
)
2363 del_channel_ban(tbData
);
2366 /* Source bans can override multiple target bans, so
2367 we allow a source to run through this loop multiple
2368 times, but we can only move it once. */
2373 /* Remove the source ban from the source ban list. */
2375 sbData
->next
->prev
= sbData
->prev
;
2377 /* Modify the source ban's associated channel. */
2378 sbData
->channel
= target
;
2380 /* Insert the ban into the target channel's linked list. */
2381 sbData
->prev
= NULL
;
2382 sbData
->next
= target
->bans
;
2385 target
->bans
->prev
= sbData
;
2386 target
->bans
= sbData
;
2388 /* Update the user counts for the target channel. */
2393 /* Possible to assert (source->bans == NULL) here. */
2394 source
->bans
= NULL
;
2398 merge_data(struct chanData
*source
, struct chanData
*target
)
2400 /* Use more recent visited and owner-transfer time; use older
2401 * registered time. Bitwise or may_opchan. Use higher max.
2402 * Do not touch last_refresh, ban count or user counts.
2404 if(source
->visited
> target
->visited
)
2405 target
->visited
= source
->visited
;
2406 if(source
->registered
< target
->registered
)
2407 target
->registered
= source
->registered
;
2408 if(source
->ownerTransfer
> target
->ownerTransfer
)
2409 target
->ownerTransfer
= source
->ownerTransfer
;
2410 if(source
->may_opchan
)
2411 target
->may_opchan
= 1;
2412 if(source
->max
> target
->max
)
2413 target
->max
= source
->max
;
2417 merge_channel(struct chanData
*source
, struct chanData
*target
)
2419 merge_users(source
, target
);
2420 merge_bans(source
, target
);
2421 merge_data(source
, target
);
2424 static CHANSERV_FUNC(cmd_merge
)
2426 struct userData
*target_user
;
2427 struct chanNode
*target
;
2428 char reason
[MAXLEN
];
2432 /* Make sure the target channel exists and is registered to the user
2433 performing the command. */
2434 if(!(target
= GetChannel(argv
[1])))
2436 reply("MSG_INVALID_CHANNEL");
2440 if(!target
->channel_info
)
2442 reply("CSMSG_NOT_REGISTERED", target
->name
);
2446 if(IsProtected(channel
->channel_info
))
2448 reply("CSMSG_MERGE_NODELETE");
2452 if(IsSuspended(target
->channel_info
))
2454 reply("CSMSG_MERGE_SUSPENDED");
2458 if(channel
== target
)
2460 reply("CSMSG_MERGE_SELF");
2464 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2465 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2467 reply("CSMSG_MERGE_NOT_OWNER");
2471 /* Merge the channel structures and associated data. */
2472 merge_channel(channel
->channel_info
, target
->channel_info
);
2473 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2474 unregister_channel(channel
->channel_info
, reason
);
2475 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2479 static CHANSERV_FUNC(cmd_opchan
)
2481 struct mod_chanmode change
;
2482 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2484 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2487 channel
->channel_info
->may_opchan
= 0;
2488 mod_chanmode_init(&change
);
2490 change
.args
[0].mode
= MODE_CHANOP
;
2491 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2492 mod_chanmode_announce(chanserv
, channel
, &change
);
2493 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2497 static CHANSERV_FUNC(cmd_adduser
)
2499 struct userData
*actee
;
2500 struct userData
*actor
;
2501 struct handle_info
*handle
;
2502 unsigned short access
;
2506 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2508 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2512 access
= user_level_from_name(argv
[2], UL_OWNER
);
2515 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2519 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2520 if(actor
->access
<= access
)
2522 reply("CSMSG_NO_BUMP_ACCESS");
2526 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2528 // 'kevin must first authenticate with AuthServ.' is sent to user
2529 struct userNode
*unode
;
2530 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2533 if(find_adduser_pending(channel
, unode
)) {
2534 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2537 if(IsInChannel(channel
, unode
)) {
2538 reply("CSMSG_ADDUSER_PENDING");
2539 add_adduser_pending(channel
, unode
, access
);
2540 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2542 /* this results in user must auth AND not in chan errors. too confusing..
2544 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2552 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2554 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2558 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2559 scan_user_presence(actee
, NULL
);
2560 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2564 static CHANSERV_FUNC(cmd_clvl
)
2566 struct handle_info
*handle
;
2567 struct userData
*victim
;
2568 struct userData
*actor
;
2569 unsigned short new_access
;
2570 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2574 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2576 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2579 if(handle
== user
->handle_info
&& !privileged
)
2581 reply("CSMSG_NO_SELF_CLVL");
2585 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2587 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2591 if(actor
->access
<= victim
->access
&& !privileged
)
2593 reply("MSG_USER_OUTRANKED", handle
->handle
);
2597 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2601 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2605 if(new_access
>= actor
->access
&& !privileged
)
2607 reply("CSMSG_NO_BUMP_ACCESS");
2611 victim
->access
= new_access
;
2612 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2616 static CHANSERV_FUNC(cmd_deluser
)
2618 struct handle_info
*handle
;
2619 struct userData
*victim
;
2620 struct userData
*actor
;
2621 unsigned short access
;
2626 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2628 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2631 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2633 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2639 access
= user_level_from_name(argv
[1], UL_OWNER
);
2642 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2645 if(access
!= victim
->access
)
2647 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2653 access
= victim
->access
;
2656 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2658 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2662 chan_name
= strdup(channel
->name
);
2663 del_channel_user(victim
, 1);
2664 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2670 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2672 struct userData
*actor
, *uData
, *next
;
2674 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2676 if(min_access
> max_access
)
2678 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2682 if((actor
->access
<= max_access
) && !IsHelping(user
))
2684 reply("CSMSG_NO_ACCESS");
2688 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2692 if((uData
->access
>= min_access
)
2693 && (uData
->access
<= max_access
)
2694 && match_ircglob(uData
->handle
->handle
, mask
))
2695 del_channel_user(uData
, 1);
2698 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2702 static CHANSERV_FUNC(cmd_mdelowner
)
2704 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2707 static CHANSERV_FUNC(cmd_mdelcoowner
)
2709 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2712 static CHANSERV_FUNC(cmd_mdelmanager
)
2714 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2717 static CHANSERV_FUNC(cmd_mdelop
)
2719 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2722 static CHANSERV_FUNC(cmd_mdelpeon
)
2724 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2727 static CHANSERV_FUNC(cmd_mdelhalfop
)
2729 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2735 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2737 struct banData
*bData
, *next
;
2738 char interval
[INTERVALLEN
];
2743 limit
= now
- duration
;
2744 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2748 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2751 del_channel_ban(bData
);
2755 intervalString(interval
, duration
, user
->handle_info
);
2756 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2761 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2763 struct userData
*actor
, *uData
, *next
;
2764 char interval
[INTERVALLEN
];
2768 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2769 if(min_access
> max_access
)
2771 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2775 if((actor
->access
<= max_access
) && !IsHelping(user
))
2777 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2782 limit
= now
- duration
;
2783 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2787 if((uData
->seen
> limit
) || uData
->present
)
2790 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2791 || (!max_access
&& (uData
->access
< actor
->access
)))
2793 del_channel_user(uData
, 1);
2801 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2803 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2807 static CHANSERV_FUNC(cmd_trim
)
2809 unsigned long duration
;
2810 unsigned short min_level
, max_level
;
2814 duration
= ParseInterval(argv
[2]);
2817 reply("CSMSG_CANNOT_TRIM");
2821 if(!irccasecmp(argv
[1], "lamers"))
2823 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2826 else if(!irccasecmp(argv
[1], "users"))
2828 cmd_trim_users(user
, channel
, 0, 0, duration
);
2831 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2833 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2836 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2838 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2843 reply("CSMSG_INVALID_TRIM", argv
[1]);
2848 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2849 to the user. cmd_all takes advantage of this. */
2850 static CHANSERV_FUNC(cmd_up
)
2852 struct mod_chanmode change
;
2853 struct userData
*uData
;
2856 mod_chanmode_init(&change
);
2858 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2859 if(!change
.args
[0].u
.member
)
2862 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2866 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2870 reply("CSMSG_GODMODE_UP", argv
[0]);
2873 else if(uData
->access
>= UL_OP
)
2875 change
.args
[0].mode
= MODE_CHANOP
;
2876 errmsg
= "CSMSG_ALREADY_OPPED";
2878 else if(uData
->access
>= UL_HALFOP
)
2880 change
.args
[0].mode
= MODE_HALFOP
;
2881 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2883 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2885 change
.args
[0].mode
= MODE_VOICE
;
2886 errmsg
= "CSMSG_ALREADY_VOICED";
2891 reply("CSMSG_NO_ACCESS");
2894 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2895 if(!change
.args
[0].mode
)
2898 reply(errmsg
, channel
->name
);
2901 modcmd_chanmode_announce(&change
);
2905 static CHANSERV_FUNC(cmd_down
)
2907 struct mod_chanmode change
;
2909 mod_chanmode_init(&change
);
2911 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2912 if(!change
.args
[0].u
.member
)
2915 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2919 if(!change
.args
[0].u
.member
->modes
)
2922 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2926 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2927 modcmd_chanmode_announce(&change
);
2931 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
)
2933 struct userData
*cList
;
2935 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2937 if(IsSuspended(cList
->channel
)
2938 || IsUserSuspended(cList
)
2939 || !GetUserMode(cList
->channel
->channel
, user
))
2942 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2948 static CHANSERV_FUNC(cmd_upall
)
2950 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2953 static CHANSERV_FUNC(cmd_downall
)
2955 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2958 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2959 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2962 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
)
2964 unsigned int ii
, valid
;
2965 struct userNode
*victim
;
2966 struct mod_chanmode
*change
;
2968 change
= mod_chanmode_alloc(argc
- 1);
2970 for(ii
=valid
=0; ++ii
< argc
; )
2972 if(!(victim
= GetUserH(argv
[ii
])))
2974 change
->args
[valid
].mode
= mode
;
2975 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2976 if(!change
->args
[valid
].u
.member
)
2978 if(validate
&& !validate(user
, channel
, victim
))
2983 change
->argc
= valid
;
2984 if(valid
< (argc
-1))
2985 reply("CSMSG_PROCESS_FAILED");
2988 modcmd_chanmode_announce(change
);
2989 reply(action
, channel
->name
);
2991 mod_chanmode_free(change
);
2995 static CHANSERV_FUNC(cmd_op
)
2997 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3000 static CHANSERV_FUNC(cmd_hop
)
3002 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3005 static CHANSERV_FUNC(cmd_deop
)
3007 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3010 static CHANSERV_FUNC(cmd_dehop
)
3012 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3015 static CHANSERV_FUNC(cmd_voice
)
3017 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3020 static CHANSERV_FUNC(cmd_devoice
)
3022 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3026 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3032 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3034 struct modeNode
*mn
= channel
->members
.list
[ii
];
3036 if(IsService(mn
->user
))
3039 if(!user_matches_glob(mn
->user
, ban
, 1))
3042 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3046 victims
[(*victimCount
)++] = mn
;
3052 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3054 struct userNode
*victim
;
3055 struct modeNode
**victims
;
3056 unsigned int offset
, n
, victimCount
, duration
= 0;
3057 char *reason
= "Bye.", *ban
, *name
;
3058 char interval
[INTERVALLEN
];
3060 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3061 REQUIRE_PARAMS(offset
);
3064 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3065 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3067 /* Truncate the reason to a length of TOPICLEN, as
3068 the ircd does; however, leave room for an ellipsis
3069 and the kicker's nick. */
3070 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3074 if((victim
= GetUserH(argv
[1])))
3076 victims
= alloca(sizeof(victims
[0]));
3077 victims
[0] = GetUserMode(channel
, victim
);
3078 /* XXX: The comparison with ACTION_KICK is just because all
3079 * other actions can work on users outside the channel, and we
3080 * want to allow those (e.g. unbans) in that case. If we add
3081 * some other ejection action for in-channel users, change
3083 victimCount
= victims
[0] ? 1 : 0;
3085 if(IsService(victim
))
3088 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3092 if((action
== ACTION_KICK
) && !victimCount
)
3095 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3099 if(protect_user(victim
, user
, channel
->channel_info
))
3101 // This translates to send_message(user, cmd->parent->bot, ...)
3102 // if user is x3 (ctcp action) cmd is null and segfault.
3104 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3108 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3109 name
= victim
->nick
;
3113 if(!is_ircmask(argv
[1]))
3116 reply("MSG_NICK_UNKNOWN", argv
[1]);
3120 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3122 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3125 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3128 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3129 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3131 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3132 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3133 some creativity, but its not x3's job to be the ban censor anyway. */
3134 if(is_overmask(argv
[1]))
3137 reply("CSMSG_LAME_MASK", argv
[1]);
3141 if((action
== ACTION_KICK
) && (victimCount
== 0))
3144 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3148 name
= ban
= strdup(argv
[1]);
3151 /* Truncate the ban in place if necessary; we must ensure
3152 that 'ban' is a valid ban mask before sanitizing it. */
3153 sanitize_ircmask(ban
);
3155 if(action
& ACTION_ADD_LAMER
)
3157 struct banData
*bData
, *next
;
3159 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3162 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3167 if(action
& ACTION_ADD_TIMED_LAMER
)
3169 duration
= ParseInterval(argv
[2]);
3174 reply("CSMSG_DURATION_TOO_LOW");
3178 else if(duration
> (86400 * 365 * 2))
3181 reply("CSMSG_DURATION_TOO_HIGH");
3188 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3190 if(match_ircglobs(bData
->mask
, ban
))
3192 int exact
= !irccasecmp(bData
->mask
, ban
);
3194 /* The ban is redundant; there is already a ban
3195 with the same effect in place. */
3199 free(bData
->reason
);
3200 bData
->reason
= strdup(reason
);
3201 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3203 reply("CSMSG_REASON_CHANGE", ban
);
3207 if(exact
&& bData
->expires
)
3211 /* If the ban matches an existing one exactly,
3212 extend the expiration time if the provided
3213 duration is longer. */
3214 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3216 bData
->expires
= now
+ duration
;
3227 /* Delete the expiration timeq entry and
3228 requeue if necessary. */
3229 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3232 timeq_add(bData
->expires
, expire_ban
, bData
);
3236 /* automated kickban, dont reply */
3239 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3241 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3247 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3254 if(match_ircglobs(ban
, bData
->mask
))
3256 /* The ban we are adding makes previously existing
3257 bans redundant; silently remove them. */
3258 del_channel_ban(bData
);
3262 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
);
3264 name
= ban
= strdup(bData
->mask
);
3268 /* WHAT DOES THIS DO?? -Rubin */
3269 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3271 extern const char *hidden_host_suffix
;
3272 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3274 unsigned int l1
, l2
;
3277 l2
= strlen(old_name
);
3280 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3282 new_mask
= malloc(MAXLEN
);
3283 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3285 name
= ban
= new_mask
;
3290 if(action
& ACTION_BAN
)
3292 unsigned int exists
;
3293 struct mod_chanmode
*change
;
3295 if(channel
->banlist
.used
>= MAXBANS
)
3298 reply("CSMSG_BANLIST_FULL", channel
->name
);
3303 exists
= ChannelBanExists(channel
, ban
);
3304 change
= mod_chanmode_alloc(victimCount
+ 1);
3305 for(n
= 0; n
< victimCount
; ++n
)
3307 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3308 change
->args
[n
].u
.member
= victims
[n
];
3312 change
->args
[n
].mode
= MODE_BAN
;
3313 change
->args
[n
++].u
.hostmask
= ban
;
3317 modcmd_chanmode_announce(change
);
3319 mod_chanmode_announce(chanserv
, channel
, change
);
3320 mod_chanmode_free(change
);
3322 if(exists
&& (action
== ACTION_BAN
))
3325 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3331 if(action
& ACTION_KICK
)
3333 char kick_reason
[MAXLEN
];
3334 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3336 for(n
= 0; n
< victimCount
; n
++)
3337 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3342 /* No response, since it was automated. */
3344 else if(action
& ACTION_ADD_LAMER
)
3347 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3349 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3351 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3352 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3353 else if(action
& ACTION_BAN
)
3354 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3355 else if(action
& ACTION_KICK
&& victimCount
)
3356 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3362 static CHANSERV_FUNC(cmd_kickban
)
3364 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3367 static CHANSERV_FUNC(cmd_kick
)
3369 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3372 static CHANSERV_FUNC(cmd_ban
)
3374 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3377 static CHANSERV_FUNC(cmd_addlamer
)
3379 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3382 static CHANSERV_FUNC(cmd_addtimedlamer
)
3384 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3387 static struct mod_chanmode
*
3388 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3390 struct mod_chanmode
*change
;
3391 unsigned char *match
;
3392 unsigned int ii
, count
;
3394 match
= alloca(bans
->used
);
3397 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3399 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3406 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3408 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3415 change
= mod_chanmode_alloc(count
);
3416 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3420 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3421 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3423 assert(count
== change
->argc
);
3427 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3429 unsigned int jj
, ii
, count
;
3431 struct chanData
*channel
;
3433 struct mod_chanmode
*change
;
3435 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3436 /* Walk through every channel */
3437 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3438 switch(channel
->chOpts
[chBanTimeout
])
3440 default: case '0': continue; /* Dont remove bans in this chan */
3441 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3442 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3443 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3444 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3445 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3448 /* First find out how many bans were going to unset */
3449 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3450 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3454 /* At least one ban, so setup a removal */
3455 change
= mod_chanmode_alloc(count
);
3457 /* Walk over every ban in this channel.. */
3458 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3459 bn
= channel
->channel
->banlist
.list
[jj
];
3460 if (bn
->set
< bantimeout
) {
3461 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3463 /* Add this ban to the mode change */
3464 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3465 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3467 /* Pull this ban out of the list */
3468 banList_remove(&(channel
->channel
->banlist
), bn
);
3473 /* Send the modes to IRC */
3474 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3476 /* free memory from strdup above */
3477 for(ii
= 0; ii
< count
; ++ii
)
3478 free((char*)change
->args
[ii
].u
.hostmask
);
3480 mod_chanmode_free(change
);
3483 /* Set this function to run again */
3484 if(chanserv_conf
.ban_timeout_frequency
)
3485 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3490 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3492 struct userNode
*actee
;
3498 /* may want to allow a comma delimited list of users... */
3499 if(!(actee
= GetUserH(argv
[1])))
3501 if(!is_ircmask(argv
[1]))
3503 reply("MSG_NICK_UNKNOWN", argv
[1]);
3507 mask
= strdup(argv
[1]);
3510 /* We don't sanitize the mask here because ircu
3512 if(action
& ACTION_UNBAN
)
3514 struct mod_chanmode
*change
;
3515 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3520 modcmd_chanmode_announce(change
);
3521 for(ii
= 0; ii
< change
->argc
; ++ii
)
3522 free((char*)change
->args
[ii
].u
.hostmask
);
3523 mod_chanmode_free(change
);
3528 if(action
& ACTION_DEL_LAMER
)
3530 struct banData
*ban
, *next
;
3532 ban
= channel
->channel_info
->bans
; /* lamers */
3536 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3539 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3544 del_channel_ban(ban
);
3551 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3553 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3559 static CHANSERV_FUNC(cmd_unban
)
3561 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3564 static CHANSERV_FUNC(cmd_dellamer
)
3566 /* it doesn't necessarily have to remove the channel ban - may want
3567 to make that an option. */
3568 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3571 static CHANSERV_FUNC(cmd_unbanme
)
3573 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3574 long flags
= ACTION_UNBAN
;
3576 /* remove permanent bans if the user has the proper access. */
3577 if(uData
->access
>= UL_MANAGER
)
3578 flags
|= ACTION_DEL_LAMER
;
3580 argv
[1] = user
->nick
;
3581 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3584 static CHANSERV_FUNC(cmd_unbanall
)
3586 struct mod_chanmode
*change
;
3589 if(!channel
->banlist
.used
)
3591 reply("CSMSG_NO_BANS", channel
->name
);
3595 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3596 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3598 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3599 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3601 modcmd_chanmode_announce(change
);
3602 for(ii
= 0; ii
< change
->argc
; ++ii
)
3603 free((char*)change
->args
[ii
].u
.hostmask
);
3604 mod_chanmode_free(change
);
3605 reply("CSMSG_BANS_REMOVED", channel
->name
);
3609 static CHANSERV_FUNC(cmd_open
)
3611 struct mod_chanmode
*change
;
3614 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3616 change
= mod_chanmode_alloc(0);
3617 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3618 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3619 && channel
->channel_info
->modes
.modes_set
)
3620 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3621 modcmd_chanmode_announce(change
);
3622 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3623 for(ii
= 0; ii
< change
->argc
; ++ii
)
3624 free((char*)change
->args
[ii
].u
.hostmask
);
3625 mod_chanmode_free(change
);
3629 static CHANSERV_FUNC(cmd_myaccess
)
3631 static struct string_buffer sbuf
;
3632 struct handle_info
*target_handle
;
3633 struct userData
*uData
;
3636 target_handle
= user
->handle_info
;
3637 else if(!IsHelping(user
))
3639 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3642 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3645 if(!target_handle
->channels
)
3647 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3651 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3652 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3654 struct chanData
*cData
= uData
->channel
;
3656 if(uData
->access
> UL_OWNER
)
3658 if(IsProtected(cData
)
3659 && (target_handle
!= user
->handle_info
)
3660 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3663 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3664 if(uData
->flags
== USER_AUTO_OP
)
3665 string_buffer_append(&sbuf
, ',');
3666 if(IsUserSuspended(uData
))
3667 string_buffer_append(&sbuf
, 's');
3668 if(IsUserAutoOp(uData
))
3670 if(uData
->access
>= UL_OP
)
3671 string_buffer_append(&sbuf
, 'o');
3672 else if(uData
->access
>= UL_HALFOP
)
3673 string_buffer_append(&sbuf
, 'h');
3674 else if(uData
->access
>= UL_PEON
)
3675 string_buffer_append(&sbuf
, 'v');
3677 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3678 string_buffer_append(&sbuf
, 'i');
3680 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3682 string_buffer_append_string(&sbuf
, ")]");
3683 string_buffer_append(&sbuf
, '\0');
3684 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3690 static CHANSERV_FUNC(cmd_access
)
3692 struct userNode
*target
;
3693 struct handle_info
*target_handle
;
3694 struct userData
*uData
;
3696 char prefix
[MAXLEN
];
3701 target_handle
= target
->handle_info
;
3703 else if((target
= GetUserH(argv
[1])))
3705 target_handle
= target
->handle_info
;
3707 else if(argv
[1][0] == '*')
3709 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3711 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3717 reply("MSG_NICK_UNKNOWN", argv
[1]);
3721 assert(target
|| target_handle
);
3723 if(target
== chanserv
)
3725 reply("CSMSG_IS_CHANSERV");
3733 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3738 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3741 reply("MSG_AUTHENTICATE");
3747 const char *epithet
= NULL
, *type
= NULL
;
3750 epithet
= chanserv_conf
.irc_operator_epithet
;
3753 else if(IsNetworkHelper(target
))
3755 epithet
= chanserv_conf
.network_helper_epithet
;
3756 type
= "network helper";
3758 else if(IsSupportHelper(target
))
3760 epithet
= chanserv_conf
.support_helper_epithet
;
3761 type
= "support helper";
3765 if(target_handle
->epithet
)
3766 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3768 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3770 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3774 sprintf(prefix
, "%s", target_handle
->handle
);
3777 if(!channel
->channel_info
)
3779 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3783 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3784 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3785 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3787 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3788 /* To prevent possible information leaks, only show infolines
3789 * if the requestor is in the channel or it's their own
3791 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3793 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3795 /* Likewise, only say it's suspended if the user has active
3796 * access in that channel or it's their own entry. */
3797 if(IsUserSuspended(uData
)
3798 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3799 || (user
->handle_info
== uData
->handle
)))
3801 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3806 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3812 /* This is never used...
3814 zoot_list(struct listData *list)
3816 struct userData *uData;
3817 unsigned int start, curr, highest, lowest;
3818 struct helpfile_table tmp_table;
3819 const char **temp, *msg;
3821 if(list->table.length == 1)
3824 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);
3826 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));
3827 msg = user_find_message(list->user, "MSG_NONE");
3828 send_message_type(4, list->user, list->bot, " %s", msg);
3830 tmp_table.width = list->table.width;
3831 tmp_table.flags = list->table.flags;
3832 list->table.contents[0][0] = " ";
3833 highest = list->highest;
3834 if(list->lowest != 0)
3835 lowest = list->lowest;
3836 else if(highest < 100)
3839 lowest = highest - 100;
3840 for(start = curr = 1; curr < list->table.length; )
3842 uData = list->users[curr-1];
3843 list->table.contents[curr++][0] = " ";
3844 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3847 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);
3849 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));
3850 temp = list->table.contents[--start];
3851 list->table.contents[start] = list->table.contents[0];
3852 tmp_table.contents = list->table.contents + start;
3853 tmp_table.length = curr - start;
3854 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3855 list->table.contents[start] = temp;
3857 highest = lowest - 1;
3858 lowest = (highest < 100) ? 0 : (highest - 99);
3865 def_list(struct listData
*list
)
3869 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
);
3871 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
));
3872 if(list
->table
.length
== 1)
3874 msg
= user_find_message(list
->user
, "MSG_NONE");
3875 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3878 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3882 userData_access_comp(const void *arg_a
, const void *arg_b
)
3884 const struct userData
*a
= *(struct userData
**)arg_a
;
3885 const struct userData
*b
= *(struct userData
**)arg_b
;
3887 if(a
->access
!= b
->access
)
3888 res
= b
->access
- a
->access
;
3890 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3895 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3897 void (*send_list
)(struct listData
*);
3898 struct userData
*uData
;
3899 struct listData lData
;
3900 unsigned int matches
;
3904 lData
.bot
= cmd
->parent
->bot
;
3905 lData
.channel
= channel
;
3906 lData
.lowest
= lowest
;
3907 lData
.highest
= highest
;
3908 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3909 send_list
= def_list
;
3910 /* What does the following line do exactly?? */
3911 /*(void)zoot_list; ** since it doesn't show user levels */
3913 /* this does nothing!! -rubin
3914 if(user->handle_info)
3916 switch(user->handle_info->userlist_style)
3918 case HI_STYLE_DEF: send_list = def_list; break;
3919 case HI_STYLE_ZOOT: send_list = def_list; break;
3924 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3926 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3928 if((uData
->access
< lowest
)
3929 || (uData
->access
> highest
)
3930 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3932 lData
.users
[matches
++] = uData
;
3934 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3936 lData
.table
.length
= matches
+1;
3937 lData
.table
.width
= 5;
3938 lData
.table
.flags
= TABLE_NO_FREE
;
3939 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3940 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3941 lData
.table
.contents
[0] = ary
;
3945 ary
[3] = "Last Seen";
3947 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3949 struct userData
*uData
= lData
.users
[matches
-1];
3950 char seen
[INTERVALLEN
];
3952 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3953 lData
.table
.contents
[matches
] = ary
;
3954 /* ary[0] = strtab(uData->access);*/
3955 ary
[0] = user_level_name_from_level(uData
->access
);
3956 ary
[1] = strtab(uData
->access
);
3957 ary
[2] = uData
->handle
->handle
;
3960 else if(!uData
->seen
)
3963 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3964 ary
[3] = strdup(ary
[3]);
3965 if(IsUserSuspended(uData
))
3966 ary
[4] = "Suspended";
3967 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3968 ary
[4] = "Vacation";
3973 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3975 free((char*)lData
.table
.contents
[matches
][3]);
3976 free(lData
.table
.contents
[matches
]);
3978 free(lData
.table
.contents
[0]);
3979 free(lData
.table
.contents
);
3983 /* Remove this now that debugging is over? or improve it for
3984 * users? Would it be better tied into USERS somehow? -Rubin */
3985 static CHANSERV_FUNC(cmd_pending
)
3987 struct adduserPending
*ap
;
3988 reply("CSMSG_ADDUSER_PENDING_HEADER");
3990 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3991 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3992 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3996 static CHANSERV_FUNC(cmd_users
)
3998 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4001 static CHANSERV_FUNC(cmd_wlist
)
4003 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4006 static CHANSERV_FUNC(cmd_clist
)
4008 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4011 static CHANSERV_FUNC(cmd_mlist
)
4013 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4016 static CHANSERV_FUNC(cmd_olist
)
4018 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4021 static CHANSERV_FUNC(cmd_hlist
)
4023 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4026 static CHANSERV_FUNC(cmd_plist
)
4028 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4031 static CHANSERV_FUNC(cmd_lamers
)
4033 struct helpfile_table tbl
;
4034 unsigned int matches
= 0, timed
= 0, ii
;
4035 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4036 const char *msg_never
, *triggered
, *expires
;
4037 struct banData
*ban
, **bans
; /* lamers */
4044 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4045 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4048 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4050 if(search
&& !match_ircglobs(search
, ban
->mask
))
4052 bans
[matches
++] = ban
;
4057 tbl
.length
= matches
+ 1;
4058 tbl
.width
= 4 + timed
;
4060 tbl
.flags
= TABLE_NO_FREE
;
4061 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4062 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4063 tbl
.contents
[0][0] = "Mask";
4064 tbl
.contents
[0][1] = "Set By";
4065 tbl
.contents
[0][2] = "Triggered";
4068 tbl
.contents
[0][3] = "Expires";
4069 tbl
.contents
[0][4] = "Reason";
4072 tbl
.contents
[0][3] = "Reason";
4075 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4077 free(tbl
.contents
[0]);
4082 msg_never
= user_find_message(user
, "MSG_NEVER");
4083 for(ii
= 0; ii
< matches
; )
4089 else if(ban
->expires
)
4090 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4092 expires
= msg_never
;
4095 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4097 triggered
= msg_never
;
4099 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4100 tbl
.contents
[ii
][0] = ban
->mask
;
4101 tbl
.contents
[ii
][1] = ban
->owner
;
4102 tbl
.contents
[ii
][2] = strdup(triggered
);
4105 tbl
.contents
[ii
][3] = strdup(expires
);
4106 tbl
.contents
[ii
][4] = ban
->reason
;
4109 tbl
.contents
[ii
][3] = ban
->reason
;
4111 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4112 /* reply("MSG_MATCH_COUNT", matches); */
4113 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4115 free((char*)tbl
.contents
[ii
][2]);
4117 free((char*)tbl
.contents
[ii
][3]);
4118 free(tbl
.contents
[ii
]);
4120 free(tbl
.contents
[0]);
4127 * return + if the user does NOT have the right to set the topic, and
4128 * the topic is changed.
4131 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4133 struct chanData
*cData
= channel
->channel_info
;
4134 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4136 else if(cData
->topic
)
4137 return irccasecmp(new_topic
, cData
->topic
);
4144 * Makes a givin topic fit into a givin topic mask and returns
4147 * topic_mask - the mask to conform to
4148 * topic - the topic to make conform
4149 * new_topic - the pre-allocated char* to put the new topic into
4151 * modifies: new_topic
4154 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4156 //char *topic_mask = cData->topic_mask;
4158 int pos
=0, starpos
=-1, dpos
=0, len
;
4160 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4167 strcpy(new_topic
, "");
4170 len
= strlen(topic
);
4171 if((dpos
+ len
) > TOPICLEN
)
4172 len
= TOPICLEN
+ 1 - dpos
;
4173 memcpy(new_topic
+dpos
, topic
, len
);
4177 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4178 default: new_topic
[dpos
++] = tchar
; break;
4181 if((dpos
> TOPICLEN
) || tchar
)
4183 strcpy(new_topic
, "");
4186 new_topic
[dpos
] = 0;
4190 static CHANSERV_FUNC(cmd_topic
)
4192 struct chanData
*cData
;
4195 cData
= channel
->channel_info
;
4200 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4201 reply("CSMSG_TOPIC_SET", cData
->topic
);
4205 reply("CSMSG_NO_TOPIC", channel
->name
);
4209 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4210 /* If they say "!topic *", use an empty topic. */
4211 if((topic
[0] == '*') && (topic
[1] == 0))
4214 if(bad_topic(channel
, user
, topic
))
4216 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4221 /* If there is a topicmask set, and the new topic doesnt match, make it */
4222 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4224 char *topic_mask
= cData
->topic_mask
;
4225 char new_topic
[TOPICLEN
+1];
4227 /* make a new topic fitting mask */
4228 conform_topic(topic_mask
, topic
, new_topic
);
4231 /* Topic couldnt fit into mask, was too long */
4232 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4233 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4236 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4238 else /* No mask set, just set the topic */
4239 SetChannelTopic(channel
, chanserv
, topic
, 1);
4242 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4244 /* Grab the topic and save it as the default topic. */
4246 cData
->topic
= strdup(channel
->topic
);
4252 static CHANSERV_FUNC(cmd_mode
)
4254 struct mod_chanmode
*change
;
4258 change
= &channel
->channel_info
->modes
;
4259 if(change
->modes_set
|| change
->modes_clear
) {
4260 modcmd_chanmode_announce(change
);
4261 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4263 reply("CSMSG_NO_MODES", channel
->name
);
4267 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4270 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4274 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4275 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4278 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4279 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4283 modcmd_chanmode_announce(change
);
4284 mod_chanmode_free(change
);
4285 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4289 static CHANSERV_FUNC(cmd_invite
)
4291 struct userData
*uData
;
4292 struct userNode
*invite
;
4294 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4298 if(!(invite
= GetUserH(argv
[1])))
4300 reply("MSG_NICK_UNKNOWN", argv
[1]);
4307 if(GetUserMode(channel
, invite
))
4309 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4317 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4318 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4321 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4323 irc_invite(chanserv
, invite
, channel
);
4325 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4330 static CHANSERV_FUNC(cmd_inviteme
)
4332 if(GetUserMode(channel
, user
))
4334 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4337 if(channel
->channel_info
4338 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4340 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4343 irc_invite(cmd
->parent
->bot
, user
, channel
);
4348 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4351 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4353 /* We display things based on two dimensions:
4354 * - Issue time: present or absent
4355 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4356 * (in order of precedence, so something both expired and revoked
4357 * only counts as revoked)
4359 combo
= (suspended
->issued
? 4 : 0)
4360 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4362 case 0: /* no issue time, indefinite expiration */
4363 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4365 case 1: /* no issue time, expires in future */
4366 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4367 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4369 case 2: /* no issue time, expired */
4370 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4371 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4373 case 3: /* no issue time, revoked */
4374 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4375 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4377 case 4: /* issue time set, indefinite expiration */
4378 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4379 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4381 case 5: /* issue time set, expires in future */
4382 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4383 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4384 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4386 case 6: /* issue time set, expired */
4387 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4388 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4389 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4391 case 7: /* issue time set, revoked */
4392 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4393 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4394 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4397 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4403 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4406 const char *fmt
= "%a %b %d %H:%M %Y";
4407 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4409 if(giveownership
->staff_issuer
)
4411 if(giveownership
->reason
)
4412 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4413 giveownership
->target
, giveownership
->target_access
,
4414 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4416 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4417 giveownership
->target
, giveownership
->target_access
,
4418 giveownership
->staff_issuer
, buf
);
4422 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4427 static CHANSERV_FUNC(cmd_info
)
4429 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4430 struct userData
*uData
, *owner
;
4431 struct chanData
*cData
;
4432 struct do_not_register
*dnr
;
4437 cData
= channel
->channel_info
;
4438 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4441 uData
= GetChannelUser(cData
, user
->handle_info
);
4442 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4444 mod_chanmode_format(&cData
->modes
, modes
);
4445 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4446 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4449 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4453 note
= iter_data(it
);
4454 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4457 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4458 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4461 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4462 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4463 if(owner
->access
== UL_OWNER
)
4464 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4465 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4466 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4467 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4469 privileged
= IsStaff(user
);
4471 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4472 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4473 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4475 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4476 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4478 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4480 struct suspended
*suspended
;
4481 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4482 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4483 show_suspension_info(cmd
, user
, suspended
);
4485 else if(IsSuspended(cData
))
4487 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4488 show_suspension_info(cmd
, user
, cData
->suspended
);
4490 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4492 struct giveownership
*giveownership
;
4493 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4494 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4495 show_giveownership_info(cmd
, user
, giveownership
);
4497 reply("CSMSG_CHANNEL_END");
4501 static CHANSERV_FUNC(cmd_netinfo
)
4503 extern time_t boot_time
;
4504 extern unsigned long burst_length
;
4505 char interval
[INTERVALLEN
];
4507 reply("CSMSG_NETWORK_INFO");
4508 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4509 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4510 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4511 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4512 reply("CSMSG_NETWORK_LAMERS", banCount
);
4513 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4514 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4515 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4520 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4522 struct helpfile_table table
;
4524 struct userNode
*user
;
4529 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4530 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4531 for(nn
=0; nn
<list
->used
; nn
++)
4533 user
= list
->list
[nn
];
4534 if(user
->modes
& skip_flags
)
4538 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4541 nick
= alloca(strlen(user
->nick
)+3);
4542 sprintf(nick
, "(%s)", user
->nick
);
4546 table
.contents
[table
.length
][0] = nick
;
4549 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4552 static CHANSERV_FUNC(cmd_ircops
)
4554 reply("CSMSG_STAFF_OPERS");
4555 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4559 static CHANSERV_FUNC(cmd_helpers
)
4561 reply("CSMSG_STAFF_HELPERS");
4562 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4566 static CHANSERV_FUNC(cmd_staff
)
4568 reply("CSMSG_NETWORK_STAFF");
4569 cmd_ircops(CSFUNC_ARGS
);
4570 cmd_helpers(CSFUNC_ARGS
);
4574 static CHANSERV_FUNC(cmd_peek
)
4576 struct modeNode
*mn
;
4577 char modes
[MODELEN
];
4579 struct helpfile_table table
;
4581 irc_make_chanmode(channel
, modes
);
4583 reply("CSMSG_PEEK_INFO", channel
->name
);
4585 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4586 reply("CSMSG_PEEK_MODES", modes
);
4587 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4591 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4592 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4593 for(n
= 0; n
< channel
->members
.used
; n
++)
4595 mn
= channel
->members
.list
[n
];
4596 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4598 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4599 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4604 reply("CSMSG_PEEK_OPS");
4605 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4608 reply("CSMSG_PEEK_NO_OPS");
4609 reply("CSMSG_PEEK_END");
4613 static MODCMD_FUNC(cmd_wipeinfo
)
4615 struct handle_info
*victim
;
4616 struct userData
*ud
, *actor
;
4619 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4620 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4622 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4624 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4627 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4629 reply("MSG_USER_OUTRANKED", victim
->handle
);
4635 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4639 static CHANSERV_FUNC(cmd_resync
)
4641 struct mod_chanmode
*changes
;
4642 struct chanData
*cData
= channel
->channel_info
;
4643 unsigned int ii
, used
;
4645 /* 6 = worst case -ovh+ovh on everyone */
4646 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4647 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4649 struct modeNode
*mn
= channel
->members
.list
[ii
];
4650 struct userData
*uData
;
4652 if(IsService(mn
->user
))
4656 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4658 /* If the channel is in no-mode mode, de-mode EVERYONE */
4659 if(cData
->chOpts
[chAutomode
] == 'n')
4663 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4664 changes
->args
[used
++].u
.member
= mn
;
4667 else /* Give various userlevels their modes.. */
4669 if(uData
&& uData
->access
>= UL_OP
)
4671 if(!(mn
->modes
& MODE_CHANOP
))
4673 changes
->args
[used
].mode
= MODE_CHANOP
;
4674 changes
->args
[used
++].u
.member
= mn
;
4677 else if(uData
&& uData
->access
>= UL_HALFOP
)
4679 if(mn
->modes
& MODE_CHANOP
)
4681 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4682 changes
->args
[used
++].u
.member
= mn
;
4684 if(!(mn
->modes
& MODE_HALFOP
))
4686 changes
->args
[used
].mode
= MODE_HALFOP
;
4687 changes
->args
[used
++].u
.member
= mn
;
4690 else if(uData
&& uData
->access
>= UL_PEON
)
4692 if(mn
->modes
& MODE_CHANOP
)
4694 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4695 changes
->args
[used
++].u
.member
= mn
;
4697 if(mn
->modes
& MODE_HALFOP
)
4699 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4700 changes
->args
[used
++].u
.member
= mn
;
4702 /* Don't voice peons if were in mode m */
4703 if( cData
->chOpts
[chAutomode
] == 'm')
4705 if(mn
->modes
& MODE_VOICE
)
4707 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4708 changes
->args
[used
++].u
.member
= mn
;
4711 /* otherwise, make user they do have voice */
4712 else if(!(mn
->modes
& MODE_VOICE
))
4714 changes
->args
[used
].mode
= MODE_VOICE
;
4715 changes
->args
[used
++].u
.member
= mn
;
4718 else /* They arnt on the userlist.. */
4720 /* If we voice everyone, but they dont.. */
4721 if(cData
->chOpts
[chAutomode
] == 'v')
4723 /* Remove anything except v */
4724 if(mn
->modes
& ~MODE_VOICE
)
4726 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4727 changes
->args
[used
++].u
.member
= mn
;
4730 if(!(mn
->modes
& MODE_VOICE
))
4732 changes
->args
[used
].mode
= MODE_VOICE
;
4733 changes
->args
[used
++].u
.member
= mn
;
4736 /* If we hop everyone, but they dont.. */
4737 else if(cData
->chOpts
[chAutomode
] == 'h')
4739 /* Remove anything except h */
4740 if(mn
->modes
& ~MODE_HALFOP
)
4742 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4743 changes
->args
[used
++].u
.member
= mn
;
4746 if(!(mn
->modes
& MODE_HALFOP
))
4748 changes
->args
[used
].mode
= MODE_HALFOP
;
4749 changes
->args
[used
++].u
.member
= mn
;
4752 /* If we op everyone, but they dont.. */
4753 else if(cData
->chOpts
[chAutomode
] == 'o')
4755 /* Remove anything except h */
4756 if(mn
->modes
& ~MODE_CHANOP
)
4758 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4759 changes
->args
[used
++].u
.member
= mn
;
4762 if(!(mn
->modes
& MODE_CHANOP
))
4764 changes
->args
[used
].mode
= MODE_CHANOP
;
4765 changes
->args
[used
++].u
.member
= mn
;
4768 /* they have no excuse for having modes, de-everything them */
4773 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4774 changes
->args
[used
++].u
.member
= mn
;
4780 changes
->argc
= used
;
4781 modcmd_chanmode_announce(changes
);
4782 mod_chanmode_free(changes
);
4783 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4787 static CHANSERV_FUNC(cmd_seen
)
4789 struct userData
*uData
;
4790 struct handle_info
*handle
;
4791 char seen
[INTERVALLEN
];
4795 if(!irccasecmp(argv
[1], chanserv
->nick
))
4797 reply("CSMSG_IS_CHANSERV");
4801 if(!(handle
= get_handle_info(argv
[1])))
4803 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4807 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4809 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4814 reply("CSMSG_USER_PRESENT", handle
->handle
);
4815 else if(uData
->seen
)
4816 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4818 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4820 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4821 reply("CSMSG_USER_VACATION", handle
->handle
);
4826 static MODCMD_FUNC(cmd_names
)
4828 struct userNode
*targ
;
4829 struct userData
*targData
;
4830 unsigned int ii
, pos
;
4833 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4835 targ
= channel
->members
.list
[ii
]->user
;
4836 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4839 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4842 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4846 if(IsUserSuspended(targData
))
4848 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4851 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4852 reply("CSMSG_END_NAMES", channel
->name
);
4857 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4859 switch(ntype
->visible_type
)
4861 case NOTE_VIS_ALL
: return 1;
4862 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4863 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4868 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4870 struct userData
*uData
;
4872 switch(ntype
->set_access_type
)
4874 case NOTE_SET_CHANNEL_ACCESS
:
4875 if(!user
->handle_info
)
4877 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4879 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4880 case NOTE_SET_CHANNEL_SETTER
:
4881 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4882 case NOTE_SET_PRIVILEGED
: default:
4883 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4887 static CHANSERV_FUNC(cmd_note
)
4889 struct chanData
*cData
;
4891 struct note_type
*ntype
;
4893 cData
= channel
->channel_info
;
4896 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4900 /* If no arguments, show all visible notes for the channel. */
4906 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4908 note
= iter_data(it
);
4909 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4912 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4913 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4916 reply("CSMSG_NOTELIST_END", channel
->name
);
4918 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4920 /* If one argument, show the named note. */
4923 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4924 && note_type_visible_to_user(cData
, note
->type
, user
))
4926 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4928 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4929 && note_type_visible_to_user(NULL
, ntype
, user
))
4931 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4936 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4940 /* Assume they're trying to set a note. */
4944 ntype
= dict_find(note_types
, argv
[1], NULL
);
4947 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4950 else if(note_type_settable_by_user(channel
, ntype
, user
))
4952 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4953 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4954 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4955 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4956 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4958 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4960 /* The note is viewable to staff only, so return 0
4961 to keep the invocation from getting logged (or
4962 regular users can see it in !events). */
4968 reply("CSMSG_NO_ACCESS");
4975 static CHANSERV_FUNC(cmd_delnote
)
4980 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4981 || !note_type_settable_by_user(channel
, note
->type
, user
))
4983 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4986 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4987 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4991 static CHANSERV_FUNC(cmd_last
)
4997 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
4999 if(numoflines
< 1 || numoflines
> 200)
5001 reply("CSMSG_LAST_INVALID");
5004 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5008 static CHANSERV_FUNC(cmd_events
)
5010 struct logSearch discrim
;
5011 struct logReport report
;
5012 unsigned int matches
, limit
;
5014 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5015 if(limit
< 1 || limit
> 200)
5018 memset(&discrim
, 0, sizeof(discrim
));
5019 discrim
.masks
.bot
= chanserv
;
5020 discrim
.masks
.channel_name
= channel
->name
;
5022 discrim
.masks
.command
= argv
[2];
5023 discrim
.limit
= limit
;
5024 discrim
.max_time
= INT_MAX
;
5025 discrim
.severities
= 1 << LOG_COMMAND
;
5026 report
.reporter
= chanserv
;
5028 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5030 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5032 reply("MSG_MATCH_COUNT", matches
);
5034 reply("MSG_NO_MATCHES");
5038 static CHANSERV_FUNC(cmd_say
)
5044 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5045 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5047 else if(GetUserH(argv
[1]))
5050 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5051 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5055 reply("MSG_NOT_TARGET_NAME");
5061 static CHANSERV_FUNC(cmd_emote
)
5067 /* CTCP is so annoying. */
5068 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5069 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5071 else if(GetUserH(argv
[1]))
5073 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5074 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5078 reply("MSG_NOT_TARGET_NAME");
5084 struct channelList
*
5085 chanserv_support_channels(void)
5087 return &chanserv_conf
.support_channels
;
5090 static CHANSERV_FUNC(cmd_expire
)
5092 int channel_count
= registered_channels
;
5093 expire_channels(NULL
);
5094 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5099 chanserv_expire_suspension(void *data
)
5101 struct suspended
*suspended
= data
;
5102 struct chanNode
*channel
;
5104 if(!suspended
->expires
|| (now
< suspended
->expires
))
5105 suspended
->revoked
= now
;
5106 channel
= suspended
->cData
->channel
;
5107 suspended
->cData
->channel
= channel
;
5108 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5109 if(!IsOffChannel(suspended
->cData
))
5111 struct mod_chanmode change
;
5112 mod_chanmode_init(&change
);
5114 change
.args
[0].mode
= MODE_CHANOP
;
5115 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5116 mod_chanmode_announce(chanserv
, channel
, &change
);
5120 static CHANSERV_FUNC(cmd_csuspend
)
5122 struct suspended
*suspended
;
5123 char reason
[MAXLEN
];
5124 time_t expiry
, duration
;
5125 struct userData
*uData
;
5129 if(IsProtected(channel
->channel_info
))
5131 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5135 if(argv
[1][0] == '!')
5137 else if(IsSuspended(channel
->channel_info
))
5139 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5140 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5144 if(!strcmp(argv
[1], "0"))
5146 else if((duration
= ParseInterval(argv
[1])))
5147 expiry
= now
+ duration
;
5150 reply("MSG_INVALID_DURATION", argv
[1]);
5154 unsplit_string(argv
+ 2, argc
- 2, reason
);
5156 suspended
= calloc(1, sizeof(*suspended
));
5157 suspended
->revoked
= 0;
5158 suspended
->issued
= now
;
5159 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5160 suspended
->expires
= expiry
;
5161 suspended
->reason
= strdup(reason
);
5162 suspended
->cData
= channel
->channel_info
;
5163 suspended
->previous
= suspended
->cData
->suspended
;
5164 suspended
->cData
->suspended
= suspended
;
5166 if(suspended
->expires
)
5167 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5169 if(IsSuspended(channel
->channel_info
))
5171 suspended
->previous
->revoked
= now
;
5172 if(suspended
->previous
->expires
)
5173 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5174 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5175 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5179 /* Mark all users in channel as absent. */
5180 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5189 /* Mark the channel as suspended, then part. */
5190 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5191 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5192 reply("CSMSG_SUSPENDED", channel
->name
);
5193 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5194 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5199 static CHANSERV_FUNC(cmd_cunsuspend
)
5201 struct suspended
*suspended
;
5202 char message
[MAXLEN
];
5204 if(!IsSuspended(channel
->channel_info
))
5206 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5210 suspended
= channel
->channel_info
->suspended
;
5212 /* Expire the suspension and join ChanServ to the channel. */
5213 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5214 chanserv_expire_suspension(suspended
);
5215 reply("CSMSG_UNSUSPENDED", channel
->name
);
5216 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5217 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5221 typedef struct chanservSearch
5229 unsigned long flags
;
5233 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5236 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5241 search
= malloc(sizeof(struct chanservSearch
));
5242 memset(search
, 0, sizeof(*search
));
5245 for(i
= 0; i
< argc
; i
++)
5247 /* Assume all criteria require arguments. */
5250 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5254 if(!irccasecmp(argv
[i
], "name"))
5255 search
->name
= argv
[++i
];
5256 else if(!irccasecmp(argv
[i
], "registrar"))
5257 search
->registrar
= argv
[++i
];
5258 else if(!irccasecmp(argv
[i
], "unvisited"))
5259 search
->unvisited
= ParseInterval(argv
[++i
]);
5260 else if(!irccasecmp(argv
[i
], "registered"))
5261 search
->registered
= ParseInterval(argv
[++i
]);
5262 else if(!irccasecmp(argv
[i
], "flags"))
5265 if(!irccasecmp(argv
[i
], "nodelete"))
5266 search
->flags
|= CHANNEL_NODELETE
;
5267 else if(!irccasecmp(argv
[i
], "suspended"))
5268 search
->flags
|= CHANNEL_SUSPENDED
;
5271 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5275 else if(!irccasecmp(argv
[i
], "limit"))
5276 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5279 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5284 if(search
->name
&& !strcmp(search
->name
, "*"))
5286 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5287 search
->registrar
= 0;
5296 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5298 const char *name
= channel
->channel
->name
;
5299 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5300 (search
->registrar
&& !channel
->registrar
) ||
5301 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5302 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5303 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5304 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5311 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5313 struct chanData
*channel
;
5314 unsigned int matches
= 0;
5316 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5318 if(!chanserv_channel_match(channel
, search
))
5328 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5333 search_print(struct chanData
*channel
, void *data
)
5335 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5338 static CHANSERV_FUNC(cmd_search
)
5341 unsigned int matches
;
5342 channel_search_func action
;
5346 if(!irccasecmp(argv
[1], "count"))
5347 action
= search_count
;
5348 else if(!irccasecmp(argv
[1], "print"))
5349 action
= search_print
;
5352 reply("CSMSG_ACTION_INVALID", argv
[1]);
5356 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5360 if(action
== search_count
)
5361 search
->limit
= INT_MAX
;
5363 if(action
== search_print
)
5365 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5369 matches
= chanserv_channel_search(search
, action
, user
);
5372 reply("MSG_MATCH_COUNT", matches
);
5374 reply("MSG_NO_MATCHES");
5380 static CHANSERV_FUNC(cmd_unvisited
)
5382 struct chanData
*cData
;
5383 time_t interval
= chanserv_conf
.channel_expire_delay
;
5384 char buffer
[INTERVALLEN
];
5385 unsigned int limit
= 25, matches
= 0;
5389 interval
= ParseInterval(argv
[1]);
5391 limit
= atoi(argv
[2]);
5394 intervalString(buffer
, interval
, user
->handle_info
);
5395 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5397 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5399 if((now
- cData
->visited
) < interval
)
5402 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5403 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5410 static MODCMD_FUNC(chan_opt_defaulttopic
)
5416 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5418 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5422 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5424 free(channel
->channel_info
->topic
);
5425 if(topic
[0] == '*' && topic
[1] == 0)
5427 topic
= channel
->channel_info
->topic
= NULL
;
5431 topic
= channel
->channel_info
->topic
= strdup(topic
);
5432 if(channel
->channel_info
->topic_mask
5433 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5434 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5436 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5439 if(channel
->channel_info
->topic
)
5440 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5442 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5446 static MODCMD_FUNC(chan_opt_topicmask
)
5450 struct chanData
*cData
= channel
->channel_info
;
5453 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5455 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5459 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5461 if(cData
->topic_mask
)
5462 free(cData
->topic_mask
);
5463 if(mask
[0] == '*' && mask
[1] == 0)
5465 cData
->topic_mask
= 0;
5469 cData
->topic_mask
= strdup(mask
);
5471 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5472 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5473 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5477 if(channel
->channel_info
->topic_mask
)
5478 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5480 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5484 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5488 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5492 if(greeting
[0] == '*' && greeting
[1] == 0)
5496 unsigned int length
= strlen(greeting
);
5497 if(length
> chanserv_conf
.greeting_length
)
5499 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5502 *data
= strdup(greeting
);
5511 reply(name
, user_find_message(user
, "MSG_NONE"));
5515 static MODCMD_FUNC(chan_opt_greeting
)
5517 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5520 static MODCMD_FUNC(chan_opt_usergreeting
)
5522 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5525 static MODCMD_FUNC(chan_opt_modes
)
5527 struct mod_chanmode
*new_modes
;
5528 char modes
[MODELEN
];
5532 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5534 reply("CSMSG_NO_ACCESS");
5537 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5539 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5541 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5543 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5546 else if(new_modes
->argc
> 1)
5548 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5549 mod_chanmode_free(new_modes
);
5554 channel
->channel_info
->modes
= *new_modes
;
5555 modcmd_chanmode_announce(new_modes
);
5556 mod_chanmode_free(new_modes
);
5560 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5562 reply("CSMSG_SET_MODES", modes
);
5564 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5568 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5570 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5572 struct chanData
*cData
= channel
->channel_info
;
5577 /* Set flag according to value. */
5578 if(enabled_string(argv
[1]))
5580 cData
->flags
|= mask
;
5583 else if(disabled_string(argv
[1]))
5585 cData
->flags
&= ~mask
;
5590 reply("MSG_INVALID_BINARY", argv
[1]);
5596 /* Find current option value. */
5597 value
= (cData
->flags
& mask
) ? 1 : 0;
5601 reply(name
, user_find_message(user
, "MSG_ON"));
5603 reply(name
, user_find_message(user
, "MSG_OFF"));
5607 static MODCMD_FUNC(chan_opt_nodelete
)
5609 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5611 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5615 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5618 static MODCMD_FUNC(chan_opt_dynlimit
)
5620 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5623 static MODCMD_FUNC(chan_opt_offchannel
)
5625 struct chanData
*cData
= channel
->channel_info
;
5630 /* Set flag according to value. */
5631 if(enabled_string(argv
[1]))
5633 if(!IsOffChannel(cData
))
5634 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5635 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5638 else if(disabled_string(argv
[1]))
5640 if(IsOffChannel(cData
))
5642 struct mod_chanmode change
;
5643 mod_chanmode_init(&change
);
5645 change
.args
[0].mode
= MODE_CHANOP
;
5646 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5647 mod_chanmode_announce(chanserv
, channel
, &change
);
5649 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5654 reply("MSG_INVALID_BINARY", argv
[1]);
5660 /* Find current option value. */
5661 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5665 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5667 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5671 static MODCMD_FUNC(chan_opt_defaults
)
5673 struct userData
*uData
;
5674 struct chanData
*cData
;
5675 const char *confirm
;
5676 enum levelOption lvlOpt
;
5677 enum charOption chOpt
;
5679 cData
= channel
->channel_info
;
5680 uData
= GetChannelUser(cData
, user
->handle_info
);
5681 if(!uData
|| (uData
->access
< UL_OWNER
))
5683 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5686 confirm
= make_confirmation_string(uData
);
5687 if((argc
< 2) || strcmp(argv
[1], confirm
))
5689 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5692 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5693 cData
->modes
= chanserv_conf
.default_modes
;
5694 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5695 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5696 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5697 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5698 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5703 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5705 struct chanData
*cData
= channel
->channel_info
;
5706 struct userData
*uData
;
5707 unsigned short value
;
5711 if(!check_user_level(channel
, user
, option
, 1, 1))
5713 reply("CSMSG_CANNOT_SET");
5716 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5717 if(!value
&& strcmp(argv
[1], "0"))
5719 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5722 uData
= GetChannelUser(cData
, user
->handle_info
);
5723 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5725 reply("CSMSG_BAD_SETLEVEL");
5731 /* This test only applies to owners, since non-owners
5732 * trying to set an option to above their level get caught
5733 * by the CSMSG_BAD_SETLEVEL test above.
5735 if(value
> uData
->access
)
5737 reply("CSMSG_BAD_SETTERS");
5744 cData
->lvlOpts
[option
] = value
;
5746 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5750 static MODCMD_FUNC(chan_opt_enfops
)
5752 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5755 static MODCMD_FUNC(chan_opt_enfhalfops
)
5757 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5759 static MODCMD_FUNC(chan_opt_enfmodes
)
5761 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5764 static MODCMD_FUNC(chan_opt_enftopic
)
5766 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5769 static MODCMD_FUNC(chan_opt_pubcmd
)
5771 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5774 static MODCMD_FUNC(chan_opt_setters
)
5776 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5779 static MODCMD_FUNC(chan_opt_userinfo
)
5781 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5784 static MODCMD_FUNC(chan_opt_topicsnarf
)
5786 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5789 static MODCMD_FUNC(chan_opt_inviteme
)
5791 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5794 /* TODO: Make look like this when no args are
5796 * -X3- -------------------------------
5797 * -X3- BanTimeout: Bans are removed:
5798 * -X3- ----- * indicates current -----
5799 * -X3- 0: [*] Never.
5800 * -X3- 1: [ ] After 10 minutes.
5801 * -X3- 2: [ ] After 2 hours.
5802 * -X3- 3: [ ] After 4 hours.
5803 * -X3- 4: [ ] After 24 hours.
5804 * -X3- 5: [ ] After one week.
5805 * -X3- ------------- End -------------
5808 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5810 struct chanData
*cData
= channel
->channel_info
;
5811 int count
= charOptions
[option
].count
, index
;
5815 index
= atoi(argv
[1]);
5817 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5819 reply("CSMSG_INVALID_NUMERIC", index
);
5820 /* Show possible values. */
5821 for(index
= 0; index
< count
; index
++)
5822 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5826 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5830 /* Find current option value. */
5833 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5837 /* Somehow, the option value is corrupt; reset it to the default. */
5838 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5843 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5847 static MODCMD_FUNC(chan_opt_automode
)
5849 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
5852 static MODCMD_FUNC(chan_opt_protect
)
5854 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5857 static MODCMD_FUNC(chan_opt_toys
)
5859 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5862 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5864 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5867 static MODCMD_FUNC(chan_opt_bantimeout
)
5869 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
5872 static MODCMD_FUNC(chan_opt_topicrefresh
)
5874 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5877 static struct svccmd_list set_shows_list
;
5880 handle_svccmd_unbind(struct svccmd
*target
) {
5882 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5883 if(target
== set_shows_list
.list
[ii
])
5884 set_shows_list
.used
= 0;
5887 static CHANSERV_FUNC(cmd_set
)
5889 struct svccmd
*subcmd
;
5893 /* Check if we need to (re-)initialize set_shows_list. */
5894 if(!set_shows_list
.used
)
5896 if(!set_shows_list
.size
)
5898 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5899 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5901 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5903 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5904 sprintf(buf
, "%s %s", argv
[0], name
);
5905 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5908 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5911 svccmd_list_append(&set_shows_list
, subcmd
);
5917 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5919 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5921 subcmd
= set_shows_list
.list
[ii
];
5922 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5924 reply("CSMSG_CHANNEL_OPTIONS_END");
5928 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5929 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5932 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5935 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5937 reply("CSMSG_NO_ACCESS");
5941 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5945 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5947 struct userData
*uData
;
5949 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5952 reply("CSMSG_NOT_USER", channel
->name
);
5958 /* Just show current option value. */
5960 else if(enabled_string(argv
[1]))
5962 uData
->flags
|= mask
;
5964 else if(disabled_string(argv
[1]))
5966 uData
->flags
&= ~mask
;
5970 reply("MSG_INVALID_BINARY", argv
[1]);
5974 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5978 static MODCMD_FUNC(user_opt_autoop
)
5980 struct userData
*uData
;
5982 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5985 reply("CSMSG_NOT_USER", channel
->name
);
5988 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5989 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5991 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5992 /* TODO: add halfops error message? or is the op one generic enough? */
5995 static MODCMD_FUNC(user_opt_autoinvite
)
5997 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6000 static MODCMD_FUNC(user_opt_info
)
6002 struct userData
*uData
;
6005 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6009 /* If they got past the command restrictions (which require access)
6010 * but fail this test, we have some fool with security override on.
6012 reply("CSMSG_NOT_USER", channel
->name
);
6019 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6020 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6022 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6025 bp
= strcspn(infoline
, "\001");
6028 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6033 if(infoline
[0] == '*' && infoline
[1] == 0)
6036 uData
->info
= strdup(infoline
);
6039 reply("CSMSG_USET_INFO", uData
->info
);
6041 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6045 struct svccmd_list uset_shows_list
;
6047 static CHANSERV_FUNC(cmd_uset
)
6049 struct svccmd
*subcmd
;
6053 /* Check if we need to (re-)initialize uset_shows_list. */
6054 if(!uset_shows_list
.used
)
6058 "AutoOp", "AutoInvite", "Info"
6061 if(!uset_shows_list
.size
)
6063 uset_shows_list
.size
= ArrayLength(options
);
6064 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6066 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6068 const char *name
= options
[ii
];
6069 sprintf(buf
, "%s %s", argv
[0], name
);
6070 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6073 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6076 svccmd_list_append(&uset_shows_list
, subcmd
);
6082 /* Do this so options are presented in a consistent order. */
6083 reply("CSMSG_USER_OPTIONS");
6084 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6085 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6089 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6090 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6093 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6097 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6100 static CHANSERV_FUNC(cmd_giveownership
)
6102 struct handle_info
*new_owner_hi
;
6103 struct userData
*new_owner
, *curr_user
;
6104 struct chanData
*cData
= channel
->channel_info
;
6105 struct do_not_register
*dnr
;
6106 struct giveownership
*giveownership
;
6107 unsigned int force
, override
;
6108 unsigned short co_access
, new_owner_old_access
;
6109 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6112 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6113 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6115 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6116 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6117 && (uData
->access
> 500)
6118 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6119 || uData
->access
< 500));
6122 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6124 struct userData
*owner
= NULL
;
6125 for(curr_user
= channel
->channel_info
->users
;
6127 curr_user
= curr_user
->next
)
6129 if(curr_user
->access
!= UL_OWNER
)
6133 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6140 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6142 char delay
[INTERVALLEN
];
6143 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6144 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6147 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6149 if(new_owner_hi
== user
->handle_info
)
6151 reply("CSMSG_NO_TRANSFER_SELF");
6154 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6159 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6163 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6167 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6169 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6172 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6173 if(!IsHelping(user
))
6174 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6176 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6180 new_owner_old_access
= new_owner
->access
;
6181 if(new_owner
->access
>= UL_COOWNER
)
6182 co_access
= new_owner
->access
;
6184 co_access
= UL_COOWNER
;
6185 new_owner
->access
= UL_OWNER
;
6187 curr_user
->access
= co_access
;
6188 cData
->ownerTransfer
= now
;
6190 giveownership
= calloc(1, sizeof(*giveownership
));
6191 giveownership
->issued
= now
;
6192 giveownership
->old_owner
= curr_user
->handle
->handle
;
6193 giveownership
->target
= new_owner_hi
->handle
;
6194 giveownership
->target_access
= new_owner_old_access
;
6197 if(argc
> (2 + force
))
6199 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6200 giveownership
->reason
= strdup(transfer_reason
);
6202 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6205 giveownership
->previous
= channel
->channel_info
->giveownership
;
6206 channel
->channel_info
->giveownership
= giveownership
;
6208 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6209 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6210 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6215 chanserv_expire_user_suspension(void *data
)
6217 struct userData
*target
= data
;
6219 target
->expires
= 0;
6220 target
->flags
&= ~USER_SUSPENDED
;
6223 static CHANSERV_FUNC(cmd_suspend
)
6225 struct handle_info
*hi
;
6226 struct userData
*self
, *target
;
6230 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6231 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6232 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6234 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6237 if(target
->access
>= self
->access
)
6239 reply("MSG_USER_OUTRANKED", hi
->handle
);
6242 if(target
->flags
& USER_SUSPENDED
)
6244 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6249 target
->present
= 0;
6252 if(!strcmp(argv
[2], "0"))
6256 unsigned int duration
;
6257 if(!(duration
= ParseInterval(argv
[2])))
6259 reply("MSG_INVALID_DURATION", argv
[2]);
6262 expiry
= now
+ duration
;
6265 target
->expires
= expiry
;
6268 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6270 target
->flags
|= USER_SUSPENDED
;
6271 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6275 static CHANSERV_FUNC(cmd_unsuspend
)
6277 struct handle_info
*hi
;
6278 struct userData
*self
, *target
;
6281 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6282 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6283 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6285 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6288 if(target
->access
>= self
->access
)
6290 reply("MSG_USER_OUTRANKED", hi
->handle
);
6293 if(!(target
->flags
& USER_SUSPENDED
))
6295 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6298 target
->flags
&= ~USER_SUSPENDED
;
6299 scan_user_presence(target
, NULL
);
6300 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6301 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6305 static MODCMD_FUNC(cmd_deleteme
)
6307 struct handle_info
*hi
;
6308 struct userData
*target
;
6309 const char *confirm_string
;
6310 unsigned short access
;
6313 hi
= user
->handle_info
;
6314 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6316 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6319 if(target
->access
== UL_OWNER
)
6321 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6324 confirm_string
= make_confirmation_string(target
);
6325 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6327 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6330 access
= target
->access
;
6331 channel_name
= strdup(channel
->name
);
6332 del_channel_user(target
, 1);
6333 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6339 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6341 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6342 struct chanData
*cData
;
6345 for(cData
= channelList
; cData
; cData
= cData
->next
)
6347 if(IsSuspended(cData
))
6349 opt
= cData
->chOpts
[chTopicRefresh
];
6352 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6355 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6356 cData
->last_refresh
= refresh_num
;
6358 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6361 static CHANSERV_FUNC(cmd_unf
)
6365 char response
[MAXLEN
];
6366 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6367 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6368 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6371 reply("CSMSG_UNF_RESPONSE");
6375 static CHANSERV_FUNC(cmd_ping
)
6379 char response
[MAXLEN
];
6380 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6381 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6382 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6385 reply("CSMSG_PING_RESPONSE");
6389 static CHANSERV_FUNC(cmd_wut
)
6393 char response
[MAXLEN
];
6394 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6395 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6396 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6399 reply("CSMSG_WUT_RESPONSE");
6404 static CHANSERV_FUNC(cmd_8ball
)
6406 unsigned int i
, j
, accum
;
6411 for(i
=1; i
<argc
; i
++)
6412 for(j
=0; argv
[i
][j
]; j
++)
6413 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6414 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6417 char response
[MAXLEN
];
6418 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6419 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6422 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6426 #else /* Use cool 8ball instead */
6428 void eightball(char *outcome
, int method
, unsigned int seed
)
6432 #define NUMOFCOLORS 18
6433 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6434 "white", "black", "grey", "brown",
6435 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6436 "fuchsia","turquoise","magenta", "cyan"};
6437 #define NUMOFLOCATIONS 50
6438 char balllocations
[50][55] = {
6439 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6440 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6441 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6442 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6443 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6444 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6445 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6446 "your bra", "your hair", "your bed", "the couch", "the wall",
6447 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6448 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6449 #define NUMOFPREPS 15
6450 char ballpreps
[50][50] = {
6451 "Near", "Somewhere near", "In", "In", "In",
6452 "In", "Hiding in", "Under", "Next to", "Over",
6453 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6454 #define NUMOFNUMS 34
6455 char ballnums
[50][50] = {
6456 "A hundred", "A thousand", "A few", "42",
6457 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6458 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6459 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6461 #define NUMOFMULTS 8
6462 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6465 * 0: normal (Not used in x3)
6472 if (method
== 1) /* A Color */
6476 answer
= (rand() % 12); /* Make sure this is the # of entries */
6479 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6481 case 1: strcpy(tmp
, "Sort of a light %s color.");
6483 case 2: strcpy(tmp
, "Dark and dreary %s.");
6485 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6487 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6489 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6491 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6493 case 10: strcpy(tmp
, "Solid %s.");
6495 case 11: strcpy(tmp
, "Transparent %s.");
6497 default: strcpy(outcome
, "An invalid random number was generated.");
6500 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6503 else if (method
== 2) /* Location */
6505 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6507 else if (method
== 3) /* Number of ___ */
6509 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6513 //Debug(DBGWARNING, "Error in 8ball.");
6518 static CHANSERV_FUNC(cmd_8ball
)
6520 char *word1
, *word2
, *word3
;
6521 static char eb
[MAXLEN
];
6522 unsigned int accum
, i
, j
;
6526 for(i
=1; i
<argc
; i
++)
6527 for(j
=0; argv
[i
][j
]; j
++)
6528 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6530 accum
+= time(NULL
)/3600;
6532 word2
= argc
>2?argv
[2]:"";
6533 word3
= argc
>3?argv
[3]:"";
6536 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6537 eightball(eb
, 1, accum
);
6538 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6539 eightball(eb
, 1, accum
);
6540 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6541 eightball(eb
, 1, accum
);
6542 /*** LOCATION *****/
6547 (strcasecmp(word1
, "where") == 0) &&
6548 (strcasecmp(word2
, "is") == 0)
6552 strcasecmp(word1
, "where's") == 0
6555 eightball(eb
, 2, accum
);
6557 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6558 eightball(eb
, 3, accum
);
6562 /* Generic 8ball question.. so pull from x3.conf srvx style */
6565 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6568 char response
[MAXLEN
];
6569 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6570 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6573 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6579 char response
[MAXLEN
];
6580 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6581 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6584 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6589 static CHANSERV_FUNC(cmd_d
)
6591 unsigned long sides
, count
, modifier
, ii
, total
;
6592 char response
[MAXLEN
], *sep
;
6596 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6606 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6607 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6611 else if((sep
[0] == '-') && isdigit(sep
[1]))
6612 modifier
= strtoul(sep
, NULL
, 10);
6613 else if((sep
[0] == '+') && isdigit(sep
[1]))
6614 modifier
= strtoul(sep
+1, NULL
, 10);
6621 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6626 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6629 for(total
= ii
= 0; ii
< count
; ++ii
)
6630 total
+= (rand() % sides
) + 1;
6633 if((count
> 1) || modifier
)
6635 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6636 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6640 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6641 sprintf(response
, fmt
, total
, sides
);
6644 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6646 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6650 static CHANSERV_FUNC(cmd_huggle
)
6652 /* CTCP must be via PRIVMSG, never notice */
6654 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6656 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6660 static CHANSERV_FUNC(cmd_calc
)
6662 char response
[MAXLEN
];
6665 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6668 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6670 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6675 chanserv_adjust_limit(void *data
)
6677 struct mod_chanmode change
;
6678 struct chanData
*cData
= data
;
6679 struct chanNode
*channel
= cData
->channel
;
6682 if(IsSuspended(cData
))
6685 cData
->limitAdjusted
= now
;
6686 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6687 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6689 if(limit
> cData
->modes
.new_limit
)
6690 limit
= cData
->modes
.new_limit
;
6691 else if(limit
== cData
->modes
.new_limit
)
6695 mod_chanmode_init(&change
);
6696 change
.modes_set
= MODE_LIMIT
;
6697 change
.new_limit
= limit
;
6698 mod_chanmode_announce(chanserv
, channel
, &change
);
6702 handle_new_channel(struct chanNode
*channel
)
6704 struct chanData
*cData
;
6706 if(!(cData
= channel
->channel_info
))
6709 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6710 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6712 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6713 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6716 /* Welcome to my worst nightmare. Warning: Read (or modify)
6717 the code below at your own risk. */
6719 handle_join(struct modeNode
*mNode
)
6721 struct mod_chanmode change
;
6722 struct userNode
*user
= mNode
->user
;
6723 struct chanNode
*channel
= mNode
->channel
;
6724 struct chanData
*cData
;
6725 struct userData
*uData
= NULL
;
6726 struct banData
*bData
;
6727 struct handle_info
*handle
;
6728 unsigned int modes
= 0, info
= 0;
6731 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6734 cData
= channel
->channel_info
;
6735 if(channel
->members
.used
> cData
->max
)
6736 cData
->max
= channel
->members
.used
;
6738 mod_chanmode_init(&change
);
6740 if(channel
->banlist
.used
< MAXBANS
)
6742 /* Not joining through a ban. */
6743 for(bData
= cData
->bans
;
6744 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6745 bData
= bData
->next
);
6749 char kick_reason
[MAXLEN
];
6750 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6752 bData
->triggered
= now
;
6753 if(bData
!= cData
->bans
)
6755 /* Shuffle the ban to the head of the list. */
6757 bData
->next
->prev
= bData
->prev
;
6759 bData
->prev
->next
= bData
->next
;
6762 bData
->next
= cData
->bans
;
6765 cData
->bans
->prev
= bData
;
6766 cData
->bans
= bData
;
6769 change
.args
[0].mode
= MODE_BAN
;
6770 change
.args
[0].u
.hostmask
= bData
->mask
;
6771 mod_chanmode_announce(chanserv
, channel
, &change
);
6772 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6777 /* ChanServ will not modify the limits in join-flooded channels.
6778 It will also skip DynLimit processing when the user (or srvx)
6779 is bursting in, because there are likely more incoming. */
6780 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6781 && !user
->uplink
->burst
6782 && !channel
->join_flooded
6783 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6785 /* The user count has begun "bumping" into the channel limit,
6786 so set a timer to raise the limit a bit. Any previous
6787 timers are removed so three incoming users within the delay
6788 results in one limit change, not three. */
6790 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6791 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6794 /* Give automodes exept during join-floods */
6795 if(!channel
->join_flooded
)
6797 if(cData
->chOpts
[chAutomode
] == 'v')
6798 modes
|= MODE_VOICE
;
6799 else if(cData
->chOpts
[chAutomode
] == 'h')
6800 modes
|= MODE_HALFOP
;
6801 else if(cData
->chOpts
[chAutomode
] == 'o')
6802 modes
|= MODE_CHANOP
;
6805 greeting
= cData
->greeting
;
6806 if(user
->handle_info
)
6808 handle
= user
->handle_info
;
6810 if(IsHelper(user
) && !IsHelping(user
))
6813 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6815 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6817 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6823 uData
= GetTrueChannelAccess(cData
, handle
);
6824 if(uData
&& !IsUserSuspended(uData
))
6826 /* non users getting automodes are handled above. */
6827 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
6829 if(uData
->access
>= UL_OP
)
6830 modes
|= MODE_CHANOP
;
6831 else if(uData
->access
>= UL_HALFOP
)
6832 modes
|= MODE_HALFOP
;
6833 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
6834 modes
|= MODE_VOICE
;
6836 if(uData
->access
>= UL_PRESENT
)
6837 cData
->visited
= now
;
6838 if(cData
->user_greeting
)
6839 greeting
= cData
->user_greeting
;
6841 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6842 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6849 if(!user
->uplink
->burst
)
6853 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
6854 if(modes & MODE_CHANOP) {
6855 modes &= ~MODE_HALFOP;
6856 modes &= ~MODE_VOICE;
6859 change
.args
[0].mode
= modes
;
6860 change
.args
[0].u
.member
= mNode
;
6861 mod_chanmode_announce(chanserv
, channel
, &change
);
6863 if(greeting
&& !user
->uplink
->burst
)
6864 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6866 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6872 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6874 struct mod_chanmode change
;
6875 struct userData
*channel
;
6876 unsigned int ii
, jj
;
6878 if(!user
->handle_info
)
6881 mod_chanmode_init(&change
);
6883 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6885 struct chanNode
*cn
;
6886 struct modeNode
*mn
;
6887 if(IsUserSuspended(channel
)
6888 || IsSuspended(channel
->channel
)
6889 || !(cn
= channel
->channel
->channel
))
6892 mn
= GetUserMode(cn
, user
);
6895 if(!IsUserSuspended(channel
)
6896 && IsUserAutoInvite(channel
)
6897 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6899 && !user
->uplink
->burst
)
6900 irc_invite(chanserv
, user
, cn
);
6904 if(channel
->access
>= UL_PRESENT
)
6905 channel
->channel
->visited
= now
;
6907 if(IsUserAutoOp(channel
))
6909 if(channel
->access
>= UL_OP
)
6910 change
.args
[0].mode
= MODE_CHANOP
;
6911 else if(channel
->access
>= UL_HALFOP
)
6912 change
.args
[0].mode
= MODE_HALFOP
;
6913 else if(channel
->access
>= UL_PEON
)
6914 change
.args
[0].mode
= MODE_VOICE
;
6916 change
.args
[0].mode
= 0;
6917 change
.args
[0].u
.member
= mn
;
6918 if(change
.args
[0].mode
)
6919 mod_chanmode_announce(chanserv
, cn
, &change
);
6922 channel
->seen
= now
;
6923 channel
->present
= 1;
6926 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6928 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6929 struct banData
*ban
;
6931 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6932 || !channel
->channel_info
6933 || IsSuspended(channel
->channel_info
))
6935 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6936 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6938 if(jj
< channel
->banlist
.used
)
6940 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6942 char kick_reason
[MAXLEN
];
6943 if(!user_matches_glob(user
, ban
->mask
, 1))
6945 change
.args
[0].mode
= MODE_BAN
;
6946 change
.args
[0].u
.hostmask
= ban
->mask
;
6947 mod_chanmode_announce(chanserv
, channel
, &change
);
6948 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6949 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6950 ban
->triggered
= now
;
6955 if(IsSupportHelper(user
))
6957 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6959 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6961 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6969 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6971 struct chanData
*cData
;
6972 struct userData
*uData
;
6974 cData
= mn
->channel
->channel_info
;
6975 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6978 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6980 /* Allow for a bit of padding so that the limit doesn't
6981 track the user count exactly, which could get annoying. */
6982 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6984 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6985 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6989 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6991 scan_user_presence(uData
, mn
->user
);
6995 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6997 unsigned int ii
, jj
;
6998 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7000 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7001 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7003 if(jj
< mn
->user
->channels
.used
)
7006 if(ii
== chanserv_conf
.support_channels
.used
)
7007 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7012 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7014 struct userData
*uData
;
7016 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7017 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7018 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7021 if(protect_user(victim
, kicker
, channel
->channel_info
))
7023 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7024 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7027 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7032 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7034 struct chanData
*cData
;
7036 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7039 cData
= channel
->channel_info
;
7040 if(bad_topic(channel
, user
, channel
->topic
))
7041 { /* User doesnt have privs to set topics. Undo it */
7042 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7043 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
7046 /* If there is a topic mask set, and the new topic doesnt match,
7047 * set the topic to mask + new_topic */
7048 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7050 char new_topic
[TOPICLEN
+1];
7051 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7054 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
7055 /* and fall through to topicsnarf code below.. */
7057 else /* Topic couldnt fit into mask, was too long */
7059 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
7060 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7061 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7065 /* With topicsnarf, grab the topic and save it as the default topic. */
7066 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7069 cData
->topic
= strdup(channel
->topic
);
7075 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7077 struct mod_chanmode
*bounce
= NULL
;
7078 unsigned int bnc
, ii
;
7081 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7084 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7085 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7087 char correct
[MAXLEN
];
7088 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7089 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7090 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7092 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7094 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7096 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7097 if(!protect_user(victim
, user
, channel
->channel_info
))
7100 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7103 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7104 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7105 if(bounce
->args
[bnc
].u
.member
)
7109 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7110 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7112 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7114 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7116 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7117 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7120 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7121 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7122 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7125 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7127 const char *ban
= change
->args
[ii
].u
.hostmask
;
7128 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7131 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7132 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7133 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7135 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7140 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7141 mod_chanmode_announce(chanserv
, channel
, bounce
);
7142 for(ii
= 0; ii
< change
->argc
; ++ii
)
7143 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7144 free((char*)bounce
->args
[ii
].u
.hostmask
);
7145 mod_chanmode_free(bounce
);
7150 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7152 struct chanNode
*channel
;
7153 struct banData
*bData
;
7154 struct mod_chanmode change
;
7155 unsigned int ii
, jj
;
7156 char kick_reason
[MAXLEN
];
7158 mod_chanmode_init(&change
);
7160 change
.args
[0].mode
= MODE_BAN
;
7161 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7163 channel
= user
->channels
.list
[ii
]->channel
;
7164 /* Need not check for bans if they're opped or voiced. */
7165 /* TODO: does this make sense in automode v, h, and o? *
7166 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7167 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7169 /* Need not check for bans unless channel registration is active. */
7170 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7172 /* Look for a matching ban already on the channel. */
7173 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7174 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
7176 /* Need not act if we found one. */
7177 if(jj
< channel
->banlist
.used
)
7179 /* Look for a matching ban in this channel. */
7180 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7182 if(!user_matches_glob(user
, bData
->mask
, 1))
7184 change
.args
[0].u
.hostmask
= bData
->mask
;
7185 mod_chanmode_announce(chanserv
, channel
, &change
);
7186 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7187 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7188 bData
->triggered
= now
;
7189 break; /* we don't need to check any more bans in the channel */
7194 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7196 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7200 dict_remove2(handle_dnrs
, old_handle
, 1);
7201 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7202 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7207 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7209 struct userNode
*h_user
;
7211 if(handle
->channels
)
7213 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7214 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7216 while(handle
->channels
)
7217 del_channel_user(handle
->channels
, 1);
7222 handle_server_link(UNUSED_ARG(struct server
*server
))
7224 struct chanData
*cData
;
7226 for(cData
= channelList
; cData
; cData
= cData
->next
)
7228 if(!IsSuspended(cData
))
7229 cData
->may_opchan
= 1;
7230 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7231 && !cData
->channel
->join_flooded
7232 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7233 < chanserv_conf
.adjust_threshold
))
7235 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7236 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7242 chanserv_conf_read(void)
7246 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7247 struct mod_chanmode
*change
;
7248 struct string_list
*strlist
;
7249 struct chanNode
*chan
;
7252 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7254 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7257 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7258 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7259 chanserv_conf
.support_channels
.used
= 0;
7260 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7262 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7264 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7267 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7269 channelList_append(&chanserv_conf
.support_channels
, chan
);
7272 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7275 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7278 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7280 channelList_append(&chanserv_conf
.support_channels
, chan
);
7282 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7283 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7284 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7285 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7286 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7287 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7288 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7289 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7290 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7291 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7292 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7293 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7294 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7295 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7296 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7297 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7298 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7299 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7300 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7301 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7302 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7303 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7304 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7305 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7306 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7308 NickChange(chanserv
, str
, 0);
7309 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7310 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7311 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7312 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7313 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7314 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7315 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7316 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7317 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7318 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7319 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7320 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7321 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7322 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7323 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7324 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7325 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7328 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7329 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7330 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
7332 chanserv_conf
.default_modes
= *change
;
7333 mod_chanmode_free(change
);
7335 free_string_list(chanserv_conf
.set_shows
);
7336 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7338 strlist
= string_list_copy(strlist
);
7341 static const char *list
[] = {
7342 /* free form text */
7343 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7344 /* options based on user level */
7345 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7346 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7347 /* multiple choice options */
7348 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7349 /* binary options */
7350 "DynLimit", "NoDelete", "BanTimeout",
7355 strlist
= alloc_string_list(ArrayLength(list
)-1);
7356 for(ii
=0; list
[ii
]; ii
++)
7357 string_list_append(strlist
, strdup(list
[ii
]));
7359 chanserv_conf
.set_shows
= strlist
;
7360 /* We don't look things up now, in case the list refers to options
7361 * defined by modules initialized after this point. Just mark the
7362 * function list as invalid, so it will be initialized.
7364 set_shows_list
.used
= 0;
7365 free_string_list(chanserv_conf
.eightball
);
7366 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7369 strlist
= string_list_copy(strlist
);
7373 strlist
= alloc_string_list(4);
7374 string_list_append(strlist
, strdup("Yes."));
7375 string_list_append(strlist
, strdup("No."));
7376 string_list_append(strlist
, strdup("Maybe so."));
7378 chanserv_conf
.eightball
= strlist
;
7379 free_string_list(chanserv_conf
.old_ban_names
);
7380 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7382 strlist
= string_list_copy(strlist
);
7384 strlist
= alloc_string_list(2);
7385 chanserv_conf
.old_ban_names
= strlist
;
7386 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7387 off_channel
= str
? atoi(str
) : 0;
7391 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7394 struct note_type
*ntype
;
7397 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7399 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7402 if(!(ntype
= chanserv_create_note_type(key
)))
7404 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7408 /* Figure out set access */
7409 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7411 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7412 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7414 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7416 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7417 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7419 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7421 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7425 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7426 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7427 ntype
->set_access
.min_opserv
= 0;
7430 /* Figure out visibility */
7431 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7432 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7433 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7434 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7435 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7436 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7437 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7438 ntype
->visible_type
= NOTE_VIS_ALL
;
7440 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7442 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7443 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7447 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7449 struct handle_info
*handle
;
7450 struct userData
*uData
;
7451 char *seen
, *inf
, *flags
, *expires
;
7453 unsigned short access
;
7455 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7457 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7461 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7462 if(access
> UL_OWNER
)
7464 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7468 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7469 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7470 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7471 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7472 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7473 handle
= get_handle_info(key
);
7476 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7480 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7481 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7482 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7484 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7486 if(uData
->expires
> now
)
7487 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7489 uData
->flags
&= ~USER_SUSPENDED
;
7492 /* Upgrade: set autoop to the inverse of noautoop */
7493 if(chanserv_read_version
< 2)
7495 /* if noautoop is true, set autoop false, and vice versa */
7496 if(uData
->flags
& USER_NOAUTO_OP
)
7497 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7499 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7500 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
);
7506 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7508 struct banData
*bData
;
7509 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7510 time_t set_time
, triggered_time
, expires_time
;
7512 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7514 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7518 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7519 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7520 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7521 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7522 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7523 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7524 if (!reason
|| !owner
)
7527 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7528 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7530 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7532 expires_time
= set_time
+ atoi(s_duration
);
7536 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7539 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7542 static struct suspended
*
7543 chanserv_read_suspended(dict_t obj
)
7545 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7549 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7550 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7551 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7552 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7553 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7554 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7555 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7556 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7557 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7558 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7562 static struct giveownership
*
7563 chanserv_read_giveownership(dict_t obj
)
7565 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7569 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7570 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7572 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7574 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7575 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7577 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7578 giveownership
->reason
= str
? strdup(str
) : NULL
;
7579 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7580 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7582 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7583 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7584 return giveownership
;
7588 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7590 struct suspended
*suspended
;
7591 struct giveownership
*giveownership
;
7592 struct mod_chanmode
*modes
;
7593 struct chanNode
*cNode
;
7594 struct chanData
*cData
;
7595 struct dict
*channel
, *obj
;
7596 char *str
, *argv
[10];
7600 channel
= hir
->d
.object
;
7602 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7605 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7608 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7611 cData
= register_channel(cNode
, str
);
7614 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7618 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7620 enum levelOption lvlOpt
;
7621 enum charOption chOpt
;
7623 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7624 cData
->flags
= atoi(str
);
7626 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7628 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7630 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7631 else if(levelOptions
[lvlOpt
].old_flag
)
7633 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7634 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7636 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7640 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7642 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7644 cData
->chOpts
[chOpt
] = str
[0];
7647 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7649 enum levelOption lvlOpt
;
7650 enum charOption chOpt
;
7653 cData
->flags
= base64toint(str
, 5);
7654 count
= strlen(str
+= 5);
7655 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7658 if(levelOptions
[lvlOpt
].old_flag
)
7660 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7661 lvl
= levelOptions
[lvlOpt
].flag_value
;
7663 lvl
= levelOptions
[lvlOpt
].default_value
;
7665 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7667 case 'c': lvl
= UL_COOWNER
; break;
7668 case 'm': lvl
= UL_MANAGER
; break;
7669 case 'n': lvl
= UL_OWNER
+1; break;
7670 case 'o': lvl
= UL_OP
; break;
7671 case 'p': lvl
= UL_PEON
; break;
7672 case 'h': lvl
= UL_HALFOP
; break;
7673 case 'w': lvl
= UL_OWNER
; break;
7674 default: lvl
= 0; break;
7676 cData
->lvlOpts
[lvlOpt
] = lvl
;
7678 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7679 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7682 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7684 suspended
= chanserv_read_suspended(obj
);
7685 cData
->suspended
= suspended
;
7686 suspended
->cData
= cData
;
7687 /* We could use suspended->expires and suspended->revoked to
7688 * set the CHANNEL_SUSPENDED flag, but we don't. */
7690 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7692 suspended
= calloc(1, sizeof(*suspended
));
7693 suspended
->issued
= 0;
7694 suspended
->revoked
= 0;
7695 suspended
->suspender
= strdup(str
);
7696 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7697 suspended
->expires
= str
? atoi(str
) : 0;
7698 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7699 suspended
->reason
= strdup(str
? str
: "No reason");
7700 suspended
->previous
= NULL
;
7701 cData
->suspended
= suspended
;
7702 suspended
->cData
= cData
;
7706 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7707 suspended
= NULL
; /* to squelch a warning */
7710 if(IsSuspended(cData
)) {
7711 if(suspended
->expires
> now
)
7712 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7713 else if(suspended
->expires
)
7714 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7717 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
7719 giveownership
= chanserv_read_giveownership(obj
);
7720 cData
->giveownership
= giveownership
;
7723 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7724 struct mod_chanmode change
;
7725 mod_chanmode_init(&change
);
7727 change
.args
[0].mode
= MODE_CHANOP
;
7728 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7729 mod_chanmode_announce(chanserv
, cNode
, &change
);
7732 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7733 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7734 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7735 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7736 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7737 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7738 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7739 cData
->max
= str
? atoi(str
) : 0;
7740 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7741 cData
->greeting
= str
? strdup(str
) : NULL
;
7742 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7743 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7744 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7745 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7746 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7747 cData
->topic
= str
? strdup(str
) : NULL
;
7749 if(!IsSuspended(cData
)
7750 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7751 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7752 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7753 cData
->modes
= *modes
;
7755 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7756 if(cData
->modes
.argc
> 1)
7757 cData
->modes
.argc
= 1;
7758 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7759 mod_chanmode_free(modes
);
7762 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7763 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7764 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7766 if(!cData
->users
&& !IsProtected(cData
))
7768 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7769 unregister_channel(cData
, "has empty user list.");
7773 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7774 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7775 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7777 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7778 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7780 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7781 struct record_data
*rd
= iter_data(it
);
7782 const char *note
, *setter
;
7784 if(rd
->type
!= RECDB_OBJECT
)
7786 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7790 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7792 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7794 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7798 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7799 if(!setter
) setter
= "<unknown>";
7800 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7808 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7810 const char *setter
, *reason
, *str
;
7811 struct do_not_register
*dnr
;
7813 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7816 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7819 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7822 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7825 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7828 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7830 dnr
->set
= atoi(str
);
7836 chanserv_version_read(struct dict
*section
)
7840 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7842 chanserv_read_version
= atoi(str
);
7843 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7847 chanserv_saxdb_read(struct dict
*database
)
7849 struct dict
*section
;
7852 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7853 chanserv_version_read(section
);
7855 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7856 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7857 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7859 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7860 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7861 chanserv_channel_read(iter_key(it
), iter_data(it
));
7863 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7864 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7865 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7871 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7873 int high_present
= 0;
7874 saxdb_start_record(ctx
, KEY_USERS
, 1);
7875 for(; uData
; uData
= uData
->next
)
7877 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7879 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7880 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7881 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7883 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7885 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
7887 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7888 saxdb_end_record(ctx
);
7890 saxdb_end_record(ctx
);
7891 return high_present
;
7895 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7899 saxdb_start_record(ctx
, KEY_BANS
, 1);
7900 for(; bData
; bData
= bData
->next
)
7902 saxdb_start_record(ctx
, bData
->mask
, 0);
7903 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7904 if(bData
->triggered
)
7905 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7907 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7909 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7911 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7912 saxdb_end_record(ctx
);
7914 saxdb_end_record(ctx
);
7918 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7920 saxdb_start_record(ctx
, name
, 0);
7921 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7922 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7924 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7926 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7928 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7930 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7931 saxdb_end_record(ctx
);
7935 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
7937 saxdb_start_record(ctx
, name
, 0);
7938 if(giveownership
->staff_issuer
)
7939 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
7940 if(giveownership
->old_owner
)
7941 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
7942 if(giveownership
->target
)
7943 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
7944 if(giveownership
->target_access
)
7945 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
7946 if(giveownership
->reason
)
7947 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
7948 if(giveownership
->issued
)
7949 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
7950 if(giveownership
->previous
)
7951 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
7952 saxdb_end_record(ctx
);
7956 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7960 enum levelOption lvlOpt
;
7961 enum charOption chOpt
;
7963 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7965 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7966 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7968 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7969 if(channel
->registrar
)
7970 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7971 if(channel
->greeting
)
7972 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7973 if(channel
->user_greeting
)
7974 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7975 if(channel
->topic_mask
)
7976 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7977 if(channel
->suspended
)
7978 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7979 if(channel
->giveownership
)
7980 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
7982 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7983 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7984 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7985 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7986 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7988 buf
[0] = channel
->chOpts
[chOpt
];
7990 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7992 saxdb_end_record(ctx
);
7994 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7996 mod_chanmode_format(&channel
->modes
, buf
);
7997 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8000 high_present
= chanserv_write_users(ctx
, channel
->users
);
8001 chanserv_write_bans(ctx
, channel
->bans
);
8003 if(dict_size(channel
->notes
))
8007 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8008 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8010 struct note
*note
= iter_data(it
);
8011 saxdb_start_record(ctx
, iter_key(it
), 0);
8012 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8013 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8014 saxdb_end_record(ctx
);
8016 saxdb_end_record(ctx
);
8019 if(channel
->ownerTransfer
)
8020 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8021 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8022 saxdb_end_record(ctx
);
8026 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8030 saxdb_start_record(ctx
, ntype
->name
, 0);
8031 switch(ntype
->set_access_type
)
8033 case NOTE_SET_CHANNEL_ACCESS
:
8034 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8036 case NOTE_SET_CHANNEL_SETTER
:
8037 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8039 case NOTE_SET_PRIVILEGED
: default:
8040 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8043 switch(ntype
->visible_type
)
8045 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8046 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8047 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8049 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8050 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8051 saxdb_end_record(ctx
);
8055 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8057 struct do_not_register
*dnr
;
8060 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8062 dnr
= iter_data(it
);
8063 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8065 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8066 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8067 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8068 saxdb_end_record(ctx
);
8073 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8076 struct chanData
*channel
;
8078 /* Version Control*/
8079 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8080 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8081 saxdb_end_record(ctx
);
8084 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8085 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8086 chanserv_write_note_type(ctx
, iter_data(it
));
8087 saxdb_end_record(ctx
);
8090 saxdb_start_record(ctx
, KEY_DNR
, 1);
8091 write_dnrs_helper(ctx
, handle_dnrs
);
8092 write_dnrs_helper(ctx
, plain_dnrs
);
8093 write_dnrs_helper(ctx
, mask_dnrs
);
8094 saxdb_end_record(ctx
);
8097 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8098 for(channel
= channelList
; channel
; channel
= channel
->next
)
8099 chanserv_write_channel(ctx
, channel
);
8100 saxdb_end_record(ctx
);
8106 chanserv_db_cleanup(void) {
8108 unreg_part_func(handle_part
);
8110 unregister_channel(channelList
, "terminating.");
8111 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8112 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8113 free(chanserv_conf
.support_channels
.list
);
8114 dict_delete(handle_dnrs
);
8115 dict_delete(plain_dnrs
);
8116 dict_delete(mask_dnrs
);
8117 dict_delete(note_types
);
8118 free_string_list(chanserv_conf
.eightball
);
8119 free_string_list(chanserv_conf
.old_ban_names
);
8120 free_string_list(chanserv_conf
.set_shows
);
8121 free(set_shows_list
.list
);
8122 free(uset_shows_list
.list
);
8125 struct userData
*helper
= helperList
;
8126 helperList
= helperList
->next
;
8131 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8132 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8133 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8136 init_chanserv(const char *nick
)
8138 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8139 conf_register_reload(chanserv_conf_read
);
8141 reg_server_link_func(handle_server_link
);
8143 reg_new_channel_func(handle_new_channel
);
8144 reg_join_func(handle_join
);
8145 reg_part_func(handle_part
);
8146 reg_kick_func(handle_kick
);
8147 reg_topic_func(handle_topic
);
8148 reg_mode_change_func(handle_mode
);
8149 reg_nick_change_func(handle_nick_change
);
8151 reg_auth_func(handle_auth
);
8152 reg_handle_rename_func(handle_rename
);
8153 reg_unreg_func(handle_unreg
);
8155 handle_dnrs
= dict_new();
8156 dict_set_free_data(handle_dnrs
, free
);
8157 plain_dnrs
= dict_new();
8158 dict_set_free_data(plain_dnrs
, free
);
8159 mask_dnrs
= dict_new();
8160 dict_set_free_data(mask_dnrs
, free
);
8162 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8163 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8164 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8165 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8166 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8167 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8168 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8169 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8170 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8171 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8173 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8175 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8176 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8178 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8179 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8180 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8181 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8182 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8184 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8185 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8186 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8187 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8188 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8189 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8191 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8192 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8193 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8194 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8196 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8197 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8198 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8199 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8200 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8201 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8202 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8203 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8204 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8205 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8207 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8208 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8209 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8210 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8211 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8212 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8213 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8214 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8215 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8216 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8217 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8218 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8219 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8220 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8222 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8223 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8224 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8225 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8227 /* if you change dellamer access, see also places
8228 * like unbanme which have manager hardcoded. */
8229 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8230 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8232 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8234 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8236 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8237 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8238 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8239 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8240 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8241 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8242 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8243 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8244 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8245 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8246 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8247 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8249 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8250 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8252 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8253 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8254 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8255 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8257 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8258 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8259 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8260 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8261 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8263 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8264 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8265 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8266 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8267 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8268 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8269 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8271 /* Channel options */
8272 DEFINE_CHANNEL_OPTION(defaulttopic
);
8273 DEFINE_CHANNEL_OPTION(topicmask
);
8274 DEFINE_CHANNEL_OPTION(greeting
);
8275 DEFINE_CHANNEL_OPTION(usergreeting
);
8276 DEFINE_CHANNEL_OPTION(modes
);
8277 DEFINE_CHANNEL_OPTION(enfops
);
8278 DEFINE_CHANNEL_OPTION(enfhalfops
);
8279 DEFINE_CHANNEL_OPTION(automode
);
8280 DEFINE_CHANNEL_OPTION(protect
);
8281 DEFINE_CHANNEL_OPTION(enfmodes
);
8282 DEFINE_CHANNEL_OPTION(enftopic
);
8283 DEFINE_CHANNEL_OPTION(pubcmd
);
8284 DEFINE_CHANNEL_OPTION(userinfo
);
8285 DEFINE_CHANNEL_OPTION(dynlimit
);
8286 DEFINE_CHANNEL_OPTION(topicsnarf
);
8287 DEFINE_CHANNEL_OPTION(nodelete
);
8288 DEFINE_CHANNEL_OPTION(toys
);
8289 DEFINE_CHANNEL_OPTION(setters
);
8290 DEFINE_CHANNEL_OPTION(topicrefresh
);
8291 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8292 DEFINE_CHANNEL_OPTION(bantimeout
);
8293 DEFINE_CHANNEL_OPTION(inviteme
);
8295 DEFINE_CHANNEL_OPTION(offchannel
);
8296 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8298 /* Alias set topic to set defaulttopic for compatibility. */
8299 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8302 DEFINE_USER_OPTION(autoinvite
);
8303 DEFINE_USER_OPTION(info
);
8304 DEFINE_USER_OPTION(autoop
);
8306 /* Alias uset autovoice to uset autoop. */
8307 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8309 note_types
= dict_new();
8310 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8313 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8314 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8315 service_register(chanserv
)->trigger
= '!';
8316 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8319 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8321 if(chanserv_conf
.channel_expire_frequency
)
8322 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8324 if(chanserv_conf
.ban_timeout_frequency
)
8325 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8327 if(chanserv_conf
.refresh_period
)
8329 time_t next_refresh
;
8330 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8331 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8334 reg_exit_func(chanserv_db_cleanup
);
8335 message_register_table(msgtab
);