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
;
4196 #ifdef WITH_PROTOCOL_P10
4200 cData
= channel
->channel_info
;
4205 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4206 reply("CSMSG_TOPIC_SET", cData
->topic
);
4210 reply("CSMSG_NO_TOPIC", channel
->name
);
4214 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4215 /* If they say "!topic *", use an empty topic. */
4216 if((topic
[0] == '*') && (topic
[1] == 0))
4219 if(bad_topic(channel
, user
, topic
))
4221 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4226 /* If there is a topicmask set, and the new topic doesnt match, make it */
4227 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4229 char *topic_mask
= cData
->topic_mask
;
4230 char new_topic
[TOPICLEN
+1];
4232 /* make a new topic fitting mask */
4233 conform_topic(topic_mask
, topic
, new_topic
);
4236 /* Topic couldnt fit into mask, was too long */
4237 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4238 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4241 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4243 else /* No mask set, just set the topic */
4244 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4247 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4249 /* Grab the topic and save it as the default topic. */
4251 cData
->topic
= strdup(channel
->topic
);
4257 static CHANSERV_FUNC(cmd_mode
)
4259 struct mod_chanmode
*change
;
4263 change
= &channel
->channel_info
->modes
;
4264 if(change
->modes_set
|| change
->modes_clear
) {
4265 modcmd_chanmode_announce(change
);
4266 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4268 reply("CSMSG_NO_MODES", channel
->name
);
4272 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4275 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4279 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4280 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4283 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4284 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4288 modcmd_chanmode_announce(change
);
4289 mod_chanmode_free(change
);
4290 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4294 static CHANSERV_FUNC(cmd_invite
)
4296 struct userData
*uData
;
4297 struct userNode
*invite
;
4299 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4303 if(!(invite
= GetUserH(argv
[1])))
4305 reply("MSG_NICK_UNKNOWN", argv
[1]);
4312 if(GetUserMode(channel
, invite
))
4314 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4322 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4323 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4326 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4328 irc_invite(chanserv
, invite
, channel
);
4330 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4335 static CHANSERV_FUNC(cmd_inviteme
)
4337 if(GetUserMode(channel
, user
))
4339 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4342 if(channel
->channel_info
4343 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4345 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4348 irc_invite(cmd
->parent
->bot
, user
, channel
);
4353 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4356 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4358 /* We display things based on two dimensions:
4359 * - Issue time: present or absent
4360 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4361 * (in order of precedence, so something both expired and revoked
4362 * only counts as revoked)
4364 combo
= (suspended
->issued
? 4 : 0)
4365 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4367 case 0: /* no issue time, indefinite expiration */
4368 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4370 case 1: /* no issue time, expires in future */
4371 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4372 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4374 case 2: /* no issue time, expired */
4375 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4376 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4378 case 3: /* no issue time, revoked */
4379 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4380 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4382 case 4: /* issue time set, indefinite expiration */
4383 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4384 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4386 case 5: /* issue time set, expires in future */
4387 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4388 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4389 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4391 case 6: /* issue time set, expired */
4392 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4393 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4394 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4396 case 7: /* issue time set, revoked */
4397 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4398 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4399 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4402 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4408 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4411 const char *fmt
= "%a %b %d %H:%M %Y";
4412 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4414 if(giveownership
->staff_issuer
)
4416 if(giveownership
->reason
)
4417 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4418 giveownership
->target
, giveownership
->target_access
,
4419 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4421 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4422 giveownership
->target
, giveownership
->target_access
,
4423 giveownership
->staff_issuer
, buf
);
4427 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4432 static CHANSERV_FUNC(cmd_info
)
4434 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4435 struct userData
*uData
, *owner
;
4436 struct chanData
*cData
;
4437 struct do_not_register
*dnr
;
4442 cData
= channel
->channel_info
;
4443 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4446 uData
= GetChannelUser(cData
, user
->handle_info
);
4447 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4449 mod_chanmode_format(&cData
->modes
, modes
);
4450 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4451 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4454 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4458 note
= iter_data(it
);
4459 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4462 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4463 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4466 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4467 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4468 if(owner
->access
== UL_OWNER
)
4469 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4470 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4471 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4472 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4474 privileged
= IsStaff(user
);
4476 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4477 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4478 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4480 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4481 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4483 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4485 struct suspended
*suspended
;
4486 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4487 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4488 show_suspension_info(cmd
, user
, suspended
);
4490 else if(IsSuspended(cData
))
4492 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4493 show_suspension_info(cmd
, user
, cData
->suspended
);
4495 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4497 struct giveownership
*giveownership
;
4498 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4499 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4500 show_giveownership_info(cmd
, user
, giveownership
);
4502 reply("CSMSG_CHANNEL_END");
4506 static CHANSERV_FUNC(cmd_netinfo
)
4508 extern time_t boot_time
;
4509 extern unsigned long burst_length
;
4510 char interval
[INTERVALLEN
];
4512 reply("CSMSG_NETWORK_INFO");
4513 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4514 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4515 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4516 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4517 reply("CSMSG_NETWORK_LAMERS", banCount
);
4518 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4519 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4520 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4525 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4527 struct helpfile_table table
;
4529 struct userNode
*user
;
4534 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4535 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4536 for(nn
=0; nn
<list
->used
; nn
++)
4538 user
= list
->list
[nn
];
4539 if(user
->modes
& skip_flags
)
4543 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4546 nick
= alloca(strlen(user
->nick
)+3);
4547 sprintf(nick
, "(%s)", user
->nick
);
4551 table
.contents
[table
.length
][0] = nick
;
4554 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4557 static CHANSERV_FUNC(cmd_ircops
)
4559 reply("CSMSG_STAFF_OPERS");
4560 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4564 static CHANSERV_FUNC(cmd_helpers
)
4566 reply("CSMSG_STAFF_HELPERS");
4567 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4571 static CHANSERV_FUNC(cmd_staff
)
4573 reply("CSMSG_NETWORK_STAFF");
4574 cmd_ircops(CSFUNC_ARGS
);
4575 cmd_helpers(CSFUNC_ARGS
);
4579 static CHANSERV_FUNC(cmd_peek
)
4581 struct modeNode
*mn
;
4582 char modes
[MODELEN
];
4584 struct helpfile_table table
;
4586 irc_make_chanmode(channel
, modes
);
4588 reply("CSMSG_PEEK_INFO", channel
->name
);
4590 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4591 reply("CSMSG_PEEK_MODES", modes
);
4592 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4596 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4597 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4598 for(n
= 0; n
< channel
->members
.used
; n
++)
4600 mn
= channel
->members
.list
[n
];
4601 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4603 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4604 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4609 reply("CSMSG_PEEK_OPS");
4610 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4613 reply("CSMSG_PEEK_NO_OPS");
4614 reply("CSMSG_PEEK_END");
4618 static MODCMD_FUNC(cmd_wipeinfo
)
4620 struct handle_info
*victim
;
4621 struct userData
*ud
, *actor
;
4624 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4625 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4627 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4629 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4632 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4634 reply("MSG_USER_OUTRANKED", victim
->handle
);
4640 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4644 static CHANSERV_FUNC(cmd_resync
)
4646 struct mod_chanmode
*changes
;
4647 struct chanData
*cData
= channel
->channel_info
;
4648 unsigned int ii
, used
;
4650 /* 6 = worst case -ovh+ovh on everyone */
4651 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4652 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4654 struct modeNode
*mn
= channel
->members
.list
[ii
];
4655 struct userData
*uData
;
4657 if(IsService(mn
->user
))
4661 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4663 /* If the channel is in no-mode mode, de-mode EVERYONE */
4664 if(cData
->chOpts
[chAutomode
] == 'n')
4668 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4669 changes
->args
[used
++].u
.member
= mn
;
4672 else /* Give various userlevels their modes.. */
4674 if(uData
&& uData
->access
>= UL_OP
)
4676 if(!(mn
->modes
& MODE_CHANOP
))
4678 changes
->args
[used
].mode
= MODE_CHANOP
;
4679 changes
->args
[used
++].u
.member
= mn
;
4682 else if(uData
&& uData
->access
>= UL_HALFOP
)
4684 if(mn
->modes
& MODE_CHANOP
)
4686 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4687 changes
->args
[used
++].u
.member
= mn
;
4689 if(!(mn
->modes
& MODE_HALFOP
))
4691 changes
->args
[used
].mode
= MODE_HALFOP
;
4692 changes
->args
[used
++].u
.member
= mn
;
4695 else if(uData
&& uData
->access
>= UL_PEON
)
4697 if(mn
->modes
& MODE_CHANOP
)
4699 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4700 changes
->args
[used
++].u
.member
= mn
;
4702 if(mn
->modes
& MODE_HALFOP
)
4704 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4705 changes
->args
[used
++].u
.member
= mn
;
4707 /* Don't voice peons if were in mode m */
4708 if( cData
->chOpts
[chAutomode
] == 'm')
4710 if(mn
->modes
& MODE_VOICE
)
4712 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4713 changes
->args
[used
++].u
.member
= mn
;
4716 /* otherwise, make user they do have voice */
4717 else if(!(mn
->modes
& MODE_VOICE
))
4719 changes
->args
[used
].mode
= MODE_VOICE
;
4720 changes
->args
[used
++].u
.member
= mn
;
4723 else /* They arnt on the userlist.. */
4725 /* If we voice everyone, but they dont.. */
4726 if(cData
->chOpts
[chAutomode
] == 'v')
4728 /* Remove anything except v */
4729 if(mn
->modes
& ~MODE_VOICE
)
4731 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4732 changes
->args
[used
++].u
.member
= mn
;
4735 if(!(mn
->modes
& MODE_VOICE
))
4737 changes
->args
[used
].mode
= MODE_VOICE
;
4738 changes
->args
[used
++].u
.member
= mn
;
4741 /* If we hop everyone, but they dont.. */
4742 else if(cData
->chOpts
[chAutomode
] == 'h')
4744 /* Remove anything except h */
4745 if(mn
->modes
& ~MODE_HALFOP
)
4747 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4748 changes
->args
[used
++].u
.member
= mn
;
4751 if(!(mn
->modes
& MODE_HALFOP
))
4753 changes
->args
[used
].mode
= MODE_HALFOP
;
4754 changes
->args
[used
++].u
.member
= mn
;
4757 /* If we op everyone, but they dont.. */
4758 else if(cData
->chOpts
[chAutomode
] == 'o')
4760 /* Remove anything except h */
4761 if(mn
->modes
& ~MODE_CHANOP
)
4763 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4764 changes
->args
[used
++].u
.member
= mn
;
4767 if(!(mn
->modes
& MODE_CHANOP
))
4769 changes
->args
[used
].mode
= MODE_CHANOP
;
4770 changes
->args
[used
++].u
.member
= mn
;
4773 /* they have no excuse for having modes, de-everything them */
4778 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4779 changes
->args
[used
++].u
.member
= mn
;
4785 changes
->argc
= used
;
4786 modcmd_chanmode_announce(changes
);
4787 mod_chanmode_free(changes
);
4788 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4792 static CHANSERV_FUNC(cmd_seen
)
4794 struct userData
*uData
;
4795 struct handle_info
*handle
;
4796 char seen
[INTERVALLEN
];
4800 if(!irccasecmp(argv
[1], chanserv
->nick
))
4802 reply("CSMSG_IS_CHANSERV");
4806 if(!(handle
= get_handle_info(argv
[1])))
4808 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4812 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4814 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4819 reply("CSMSG_USER_PRESENT", handle
->handle
);
4820 else if(uData
->seen
)
4821 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4823 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4825 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4826 reply("CSMSG_USER_VACATION", handle
->handle
);
4831 static MODCMD_FUNC(cmd_names
)
4833 struct userNode
*targ
;
4834 struct userData
*targData
;
4835 unsigned int ii
, pos
;
4838 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4840 targ
= channel
->members
.list
[ii
]->user
;
4841 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4844 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4847 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4851 if(IsUserSuspended(targData
))
4853 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4856 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4857 reply("CSMSG_END_NAMES", channel
->name
);
4862 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4864 switch(ntype
->visible_type
)
4866 case NOTE_VIS_ALL
: return 1;
4867 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4868 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4873 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4875 struct userData
*uData
;
4877 switch(ntype
->set_access_type
)
4879 case NOTE_SET_CHANNEL_ACCESS
:
4880 if(!user
->handle_info
)
4882 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4884 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4885 case NOTE_SET_CHANNEL_SETTER
:
4886 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4887 case NOTE_SET_PRIVILEGED
: default:
4888 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4892 static CHANSERV_FUNC(cmd_note
)
4894 struct chanData
*cData
;
4896 struct note_type
*ntype
;
4898 cData
= channel
->channel_info
;
4901 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4905 /* If no arguments, show all visible notes for the channel. */
4911 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4913 note
= iter_data(it
);
4914 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4917 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4918 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4921 reply("CSMSG_NOTELIST_END", channel
->name
);
4923 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4925 /* If one argument, show the named note. */
4928 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4929 && note_type_visible_to_user(cData
, note
->type
, user
))
4931 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4933 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4934 && note_type_visible_to_user(NULL
, ntype
, user
))
4936 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4941 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4945 /* Assume they're trying to set a note. */
4949 ntype
= dict_find(note_types
, argv
[1], NULL
);
4952 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4955 else if(note_type_settable_by_user(channel
, ntype
, user
))
4957 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4958 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4959 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4960 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4961 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4963 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4965 /* The note is viewable to staff only, so return 0
4966 to keep the invocation from getting logged (or
4967 regular users can see it in !events). */
4973 reply("CSMSG_NO_ACCESS");
4980 static CHANSERV_FUNC(cmd_delnote
)
4985 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4986 || !note_type_settable_by_user(channel
, note
->type
, user
))
4988 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4991 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4992 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4996 static CHANSERV_FUNC(cmd_last
)
5002 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5004 if(numoflines
< 1 || numoflines
> 200)
5006 reply("CSMSG_LAST_INVALID");
5009 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5013 static CHANSERV_FUNC(cmd_events
)
5015 struct logSearch discrim
;
5016 struct logReport report
;
5017 unsigned int matches
, limit
;
5019 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5020 if(limit
< 1 || limit
> 200)
5023 memset(&discrim
, 0, sizeof(discrim
));
5024 discrim
.masks
.bot
= chanserv
;
5025 discrim
.masks
.channel_name
= channel
->name
;
5027 discrim
.masks
.command
= argv
[2];
5028 discrim
.limit
= limit
;
5029 discrim
.max_time
= INT_MAX
;
5030 discrim
.severities
= 1 << LOG_COMMAND
;
5031 report
.reporter
= chanserv
;
5033 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5035 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5037 reply("MSG_MATCH_COUNT", matches
);
5039 reply("MSG_NO_MATCHES");
5043 static CHANSERV_FUNC(cmd_say
)
5049 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5050 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5052 else if(GetUserH(argv
[1]))
5055 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5056 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5060 reply("MSG_NOT_TARGET_NAME");
5066 static CHANSERV_FUNC(cmd_emote
)
5072 /* CTCP is so annoying. */
5073 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5074 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5076 else if(GetUserH(argv
[1]))
5078 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5079 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5083 reply("MSG_NOT_TARGET_NAME");
5089 struct channelList
*
5090 chanserv_support_channels(void)
5092 return &chanserv_conf
.support_channels
;
5095 static CHANSERV_FUNC(cmd_expire
)
5097 int channel_count
= registered_channels
;
5098 expire_channels(NULL
);
5099 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5104 chanserv_expire_suspension(void *data
)
5106 struct suspended
*suspended
= data
;
5107 struct chanNode
*channel
;
5109 if(!suspended
->expires
|| (now
< suspended
->expires
))
5110 suspended
->revoked
= now
;
5111 channel
= suspended
->cData
->channel
;
5112 suspended
->cData
->channel
= channel
;
5113 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5114 if(!IsOffChannel(suspended
->cData
))
5116 struct mod_chanmode change
;
5117 mod_chanmode_init(&change
);
5119 change
.args
[0].mode
= MODE_CHANOP
;
5120 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5121 mod_chanmode_announce(chanserv
, channel
, &change
);
5125 static CHANSERV_FUNC(cmd_csuspend
)
5127 struct suspended
*suspended
;
5128 char reason
[MAXLEN
];
5129 time_t expiry
, duration
;
5130 struct userData
*uData
;
5134 if(IsProtected(channel
->channel_info
))
5136 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5140 if(argv
[1][0] == '!')
5142 else if(IsSuspended(channel
->channel_info
))
5144 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5145 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5149 if(!strcmp(argv
[1], "0"))
5151 else if((duration
= ParseInterval(argv
[1])))
5152 expiry
= now
+ duration
;
5155 reply("MSG_INVALID_DURATION", argv
[1]);
5159 unsplit_string(argv
+ 2, argc
- 2, reason
);
5161 suspended
= calloc(1, sizeof(*suspended
));
5162 suspended
->revoked
= 0;
5163 suspended
->issued
= now
;
5164 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5165 suspended
->expires
= expiry
;
5166 suspended
->reason
= strdup(reason
);
5167 suspended
->cData
= channel
->channel_info
;
5168 suspended
->previous
= suspended
->cData
->suspended
;
5169 suspended
->cData
->suspended
= suspended
;
5171 if(suspended
->expires
)
5172 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5174 if(IsSuspended(channel
->channel_info
))
5176 suspended
->previous
->revoked
= now
;
5177 if(suspended
->previous
->expires
)
5178 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5179 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5180 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5184 /* Mark all users in channel as absent. */
5185 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5194 /* Mark the channel as suspended, then part. */
5195 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5196 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5197 reply("CSMSG_SUSPENDED", channel
->name
);
5198 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5199 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5204 static CHANSERV_FUNC(cmd_cunsuspend
)
5206 struct suspended
*suspended
;
5207 char message
[MAXLEN
];
5209 if(!IsSuspended(channel
->channel_info
))
5211 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5215 suspended
= channel
->channel_info
->suspended
;
5217 /* Expire the suspension and join ChanServ to the channel. */
5218 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5219 chanserv_expire_suspension(suspended
);
5220 reply("CSMSG_UNSUSPENDED", channel
->name
);
5221 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5222 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5226 typedef struct chanservSearch
5234 unsigned long flags
;
5238 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5241 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5246 search
= malloc(sizeof(struct chanservSearch
));
5247 memset(search
, 0, sizeof(*search
));
5250 for(i
= 0; i
< argc
; i
++)
5252 /* Assume all criteria require arguments. */
5255 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5259 if(!irccasecmp(argv
[i
], "name"))
5260 search
->name
= argv
[++i
];
5261 else if(!irccasecmp(argv
[i
], "registrar"))
5262 search
->registrar
= argv
[++i
];
5263 else if(!irccasecmp(argv
[i
], "unvisited"))
5264 search
->unvisited
= ParseInterval(argv
[++i
]);
5265 else if(!irccasecmp(argv
[i
], "registered"))
5266 search
->registered
= ParseInterval(argv
[++i
]);
5267 else if(!irccasecmp(argv
[i
], "flags"))
5270 if(!irccasecmp(argv
[i
], "nodelete"))
5271 search
->flags
|= CHANNEL_NODELETE
;
5272 else if(!irccasecmp(argv
[i
], "suspended"))
5273 search
->flags
|= CHANNEL_SUSPENDED
;
5276 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5280 else if(!irccasecmp(argv
[i
], "limit"))
5281 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5284 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5289 if(search
->name
&& !strcmp(search
->name
, "*"))
5291 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5292 search
->registrar
= 0;
5301 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5303 const char *name
= channel
->channel
->name
;
5304 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5305 (search
->registrar
&& !channel
->registrar
) ||
5306 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5307 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5308 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5309 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5316 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5318 struct chanData
*channel
;
5319 unsigned int matches
= 0;
5321 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5323 if(!chanserv_channel_match(channel
, search
))
5333 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5338 search_print(struct chanData
*channel
, void *data
)
5340 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5343 static CHANSERV_FUNC(cmd_search
)
5346 unsigned int matches
;
5347 channel_search_func action
;
5351 if(!irccasecmp(argv
[1], "count"))
5352 action
= search_count
;
5353 else if(!irccasecmp(argv
[1], "print"))
5354 action
= search_print
;
5357 reply("CSMSG_ACTION_INVALID", argv
[1]);
5361 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5365 if(action
== search_count
)
5366 search
->limit
= INT_MAX
;
5368 if(action
== search_print
)
5370 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5374 matches
= chanserv_channel_search(search
, action
, user
);
5377 reply("MSG_MATCH_COUNT", matches
);
5379 reply("MSG_NO_MATCHES");
5385 static CHANSERV_FUNC(cmd_unvisited
)
5387 struct chanData
*cData
;
5388 time_t interval
= chanserv_conf
.channel_expire_delay
;
5389 char buffer
[INTERVALLEN
];
5390 unsigned int limit
= 25, matches
= 0;
5394 interval
= ParseInterval(argv
[1]);
5396 limit
= atoi(argv
[2]);
5399 intervalString(buffer
, interval
, user
->handle_info
);
5400 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5402 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5404 if((now
- cData
->visited
) < interval
)
5407 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5408 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5415 static MODCMD_FUNC(chan_opt_defaulttopic
)
5421 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5423 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5427 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5429 free(channel
->channel_info
->topic
);
5430 if(topic
[0] == '*' && topic
[1] == 0)
5432 topic
= channel
->channel_info
->topic
= NULL
;
5436 topic
= channel
->channel_info
->topic
= strdup(topic
);
5437 if(channel
->channel_info
->topic_mask
5438 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5439 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5441 SetChannelTopic(channel
, chanserv
, chanserv
, topic
? topic
: "", 1);
5444 if(channel
->channel_info
->topic
)
5445 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5447 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5451 static MODCMD_FUNC(chan_opt_topicmask
)
5455 struct chanData
*cData
= channel
->channel_info
;
5458 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5460 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5464 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5466 if(cData
->topic_mask
)
5467 free(cData
->topic_mask
);
5468 if(mask
[0] == '*' && mask
[1] == 0)
5470 cData
->topic_mask
= 0;
5474 cData
->topic_mask
= strdup(mask
);
5476 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5477 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5478 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5482 if(channel
->channel_info
->topic_mask
)
5483 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5485 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5489 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5493 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5497 if(greeting
[0] == '*' && greeting
[1] == 0)
5501 unsigned int length
= strlen(greeting
);
5502 if(length
> chanserv_conf
.greeting_length
)
5504 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5507 *data
= strdup(greeting
);
5516 reply(name
, user_find_message(user
, "MSG_NONE"));
5520 static MODCMD_FUNC(chan_opt_greeting
)
5522 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5525 static MODCMD_FUNC(chan_opt_usergreeting
)
5527 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5530 static MODCMD_FUNC(chan_opt_modes
)
5532 struct mod_chanmode
*new_modes
;
5533 char modes
[MODELEN
];
5537 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5539 reply("CSMSG_NO_ACCESS");
5542 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5544 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5546 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5548 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5551 else if(new_modes
->argc
> 1)
5553 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5554 mod_chanmode_free(new_modes
);
5559 channel
->channel_info
->modes
= *new_modes
;
5560 modcmd_chanmode_announce(new_modes
);
5561 mod_chanmode_free(new_modes
);
5565 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5567 reply("CSMSG_SET_MODES", modes
);
5569 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5573 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5575 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5577 struct chanData
*cData
= channel
->channel_info
;
5582 /* Set flag according to value. */
5583 if(enabled_string(argv
[1]))
5585 cData
->flags
|= mask
;
5588 else if(disabled_string(argv
[1]))
5590 cData
->flags
&= ~mask
;
5595 reply("MSG_INVALID_BINARY", argv
[1]);
5601 /* Find current option value. */
5602 value
= (cData
->flags
& mask
) ? 1 : 0;
5606 reply(name
, user_find_message(user
, "MSG_ON"));
5608 reply(name
, user_find_message(user
, "MSG_OFF"));
5612 static MODCMD_FUNC(chan_opt_nodelete
)
5614 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5616 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5620 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5623 static MODCMD_FUNC(chan_opt_dynlimit
)
5625 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5628 static MODCMD_FUNC(chan_opt_offchannel
)
5630 struct chanData
*cData
= channel
->channel_info
;
5635 /* Set flag according to value. */
5636 if(enabled_string(argv
[1]))
5638 if(!IsOffChannel(cData
))
5639 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5640 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5643 else if(disabled_string(argv
[1]))
5645 if(IsOffChannel(cData
))
5647 struct mod_chanmode change
;
5648 mod_chanmode_init(&change
);
5650 change
.args
[0].mode
= MODE_CHANOP
;
5651 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5652 mod_chanmode_announce(chanserv
, channel
, &change
);
5654 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5659 reply("MSG_INVALID_BINARY", argv
[1]);
5665 /* Find current option value. */
5666 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5670 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5672 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5676 static MODCMD_FUNC(chan_opt_defaults
)
5678 struct userData
*uData
;
5679 struct chanData
*cData
;
5680 const char *confirm
;
5681 enum levelOption lvlOpt
;
5682 enum charOption chOpt
;
5684 cData
= channel
->channel_info
;
5685 uData
= GetChannelUser(cData
, user
->handle_info
);
5686 if(!uData
|| (uData
->access
< UL_OWNER
))
5688 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5691 confirm
= make_confirmation_string(uData
);
5692 if((argc
< 2) || strcmp(argv
[1], confirm
))
5694 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5697 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5698 cData
->modes
= chanserv_conf
.default_modes
;
5699 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5700 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5701 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5702 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5703 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5708 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5710 struct chanData
*cData
= channel
->channel_info
;
5711 struct userData
*uData
;
5712 unsigned short value
;
5716 if(!check_user_level(channel
, user
, option
, 1, 1))
5718 reply("CSMSG_CANNOT_SET");
5721 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5722 if(!value
&& strcmp(argv
[1], "0"))
5724 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5727 uData
= GetChannelUser(cData
, user
->handle_info
);
5728 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5730 reply("CSMSG_BAD_SETLEVEL");
5736 /* This test only applies to owners, since non-owners
5737 * trying to set an option to above their level get caught
5738 * by the CSMSG_BAD_SETLEVEL test above.
5740 if(value
> uData
->access
)
5742 reply("CSMSG_BAD_SETTERS");
5749 cData
->lvlOpts
[option
] = value
;
5751 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5755 static MODCMD_FUNC(chan_opt_enfops
)
5757 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5760 static MODCMD_FUNC(chan_opt_enfhalfops
)
5762 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5764 static MODCMD_FUNC(chan_opt_enfmodes
)
5766 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5769 static MODCMD_FUNC(chan_opt_enftopic
)
5771 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5774 static MODCMD_FUNC(chan_opt_pubcmd
)
5776 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5779 static MODCMD_FUNC(chan_opt_setters
)
5781 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5784 static MODCMD_FUNC(chan_opt_userinfo
)
5786 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5789 static MODCMD_FUNC(chan_opt_topicsnarf
)
5791 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5794 static MODCMD_FUNC(chan_opt_inviteme
)
5796 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5799 /* TODO: Make look like this when no args are
5801 * -X3- -------------------------------
5802 * -X3- BanTimeout: Bans are removed:
5803 * -X3- ----- * indicates current -----
5804 * -X3- 0: [*] Never.
5805 * -X3- 1: [ ] After 10 minutes.
5806 * -X3- 2: [ ] After 2 hours.
5807 * -X3- 3: [ ] After 4 hours.
5808 * -X3- 4: [ ] After 24 hours.
5809 * -X3- 5: [ ] After one week.
5810 * -X3- ------------- End -------------
5813 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5815 struct chanData
*cData
= channel
->channel_info
;
5816 int count
= charOptions
[option
].count
, index
;
5820 index
= atoi(argv
[1]);
5822 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5824 reply("CSMSG_INVALID_NUMERIC", index
);
5825 /* Show possible values. */
5826 for(index
= 0; index
< count
; index
++)
5827 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5831 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5835 /* Find current option value. */
5838 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5842 /* Somehow, the option value is corrupt; reset it to the default. */
5843 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5848 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5852 static MODCMD_FUNC(chan_opt_automode
)
5854 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
5857 static MODCMD_FUNC(chan_opt_protect
)
5859 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5862 static MODCMD_FUNC(chan_opt_toys
)
5864 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5867 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5869 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5872 static MODCMD_FUNC(chan_opt_bantimeout
)
5874 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
5877 static MODCMD_FUNC(chan_opt_topicrefresh
)
5879 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5882 static struct svccmd_list set_shows_list
;
5885 handle_svccmd_unbind(struct svccmd
*target
) {
5887 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5888 if(target
== set_shows_list
.list
[ii
])
5889 set_shows_list
.used
= 0;
5892 static CHANSERV_FUNC(cmd_set
)
5894 struct svccmd
*subcmd
;
5898 /* Check if we need to (re-)initialize set_shows_list. */
5899 if(!set_shows_list
.used
)
5901 if(!set_shows_list
.size
)
5903 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5904 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5906 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5908 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5909 sprintf(buf
, "%s %s", argv
[0], name
);
5910 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5913 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5916 svccmd_list_append(&set_shows_list
, subcmd
);
5922 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5924 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5926 subcmd
= set_shows_list
.list
[ii
];
5927 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5929 reply("CSMSG_CHANNEL_OPTIONS_END");
5933 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5934 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5937 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5940 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5942 reply("CSMSG_NO_ACCESS");
5946 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5950 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5952 struct userData
*uData
;
5954 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5957 reply("CSMSG_NOT_USER", channel
->name
);
5963 /* Just show current option value. */
5965 else if(enabled_string(argv
[1]))
5967 uData
->flags
|= mask
;
5969 else if(disabled_string(argv
[1]))
5971 uData
->flags
&= ~mask
;
5975 reply("MSG_INVALID_BINARY", argv
[1]);
5979 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5983 static MODCMD_FUNC(user_opt_autoop
)
5985 struct userData
*uData
;
5987 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5990 reply("CSMSG_NOT_USER", channel
->name
);
5993 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5994 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5996 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5997 /* TODO: add halfops error message? or is the op one generic enough? */
6000 static MODCMD_FUNC(user_opt_autoinvite
)
6002 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6005 static MODCMD_FUNC(user_opt_info
)
6007 struct userData
*uData
;
6010 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6014 /* If they got past the command restrictions (which require access)
6015 * but fail this test, we have some fool with security override on.
6017 reply("CSMSG_NOT_USER", channel
->name
);
6024 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6025 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6027 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6030 bp
= strcspn(infoline
, "\001");
6033 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6038 if(infoline
[0] == '*' && infoline
[1] == 0)
6041 uData
->info
= strdup(infoline
);
6044 reply("CSMSG_USET_INFO", uData
->info
);
6046 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6050 struct svccmd_list uset_shows_list
;
6052 static CHANSERV_FUNC(cmd_uset
)
6054 struct svccmd
*subcmd
;
6058 /* Check if we need to (re-)initialize uset_shows_list. */
6059 if(!uset_shows_list
.used
)
6063 "AutoOp", "AutoInvite", "Info"
6066 if(!uset_shows_list
.size
)
6068 uset_shows_list
.size
= ArrayLength(options
);
6069 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6071 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6073 const char *name
= options
[ii
];
6074 sprintf(buf
, "%s %s", argv
[0], name
);
6075 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6078 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6081 svccmd_list_append(&uset_shows_list
, subcmd
);
6087 /* Do this so options are presented in a consistent order. */
6088 reply("CSMSG_USER_OPTIONS");
6089 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6090 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6094 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6095 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6098 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6102 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6105 static CHANSERV_FUNC(cmd_giveownership
)
6107 struct handle_info
*new_owner_hi
;
6108 struct userData
*new_owner
, *curr_user
;
6109 struct chanData
*cData
= channel
->channel_info
;
6110 struct do_not_register
*dnr
;
6111 struct giveownership
*giveownership
;
6112 unsigned int force
, override
;
6113 unsigned short co_access
, new_owner_old_access
;
6114 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6117 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6118 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6120 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6121 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6122 && (uData
->access
> 500)
6123 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6124 || uData
->access
< 500));
6127 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6129 struct userData
*owner
= NULL
;
6130 for(curr_user
= channel
->channel_info
->users
;
6132 curr_user
= curr_user
->next
)
6134 if(curr_user
->access
!= UL_OWNER
)
6138 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6145 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6147 char delay
[INTERVALLEN
];
6148 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6149 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6152 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6154 if(new_owner_hi
== user
->handle_info
)
6156 reply("CSMSG_NO_TRANSFER_SELF");
6159 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6164 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6168 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6172 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6174 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6177 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6178 if(!IsHelping(user
))
6179 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6181 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6185 new_owner_old_access
= new_owner
->access
;
6186 if(new_owner
->access
>= UL_COOWNER
)
6187 co_access
= new_owner
->access
;
6189 co_access
= UL_COOWNER
;
6190 new_owner
->access
= UL_OWNER
;
6192 curr_user
->access
= co_access
;
6193 cData
->ownerTransfer
= now
;
6195 giveownership
= calloc(1, sizeof(*giveownership
));
6196 giveownership
->issued
= now
;
6197 giveownership
->old_owner
= curr_user
->handle
->handle
;
6198 giveownership
->target
= new_owner_hi
->handle
;
6199 giveownership
->target_access
= new_owner_old_access
;
6202 if(argc
> (2 + force
))
6204 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6205 giveownership
->reason
= strdup(transfer_reason
);
6207 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6210 giveownership
->previous
= channel
->channel_info
->giveownership
;
6211 channel
->channel_info
->giveownership
= giveownership
;
6213 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6214 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6215 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6220 chanserv_expire_user_suspension(void *data
)
6222 struct userData
*target
= data
;
6224 target
->expires
= 0;
6225 target
->flags
&= ~USER_SUSPENDED
;
6228 static CHANSERV_FUNC(cmd_suspend
)
6230 struct handle_info
*hi
;
6231 struct userData
*self
, *target
;
6235 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6236 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6237 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6239 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6242 if(target
->access
>= self
->access
)
6244 reply("MSG_USER_OUTRANKED", hi
->handle
);
6247 if(target
->flags
& USER_SUSPENDED
)
6249 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6254 target
->present
= 0;
6257 if(!strcmp(argv
[2], "0"))
6261 unsigned int duration
;
6262 if(!(duration
= ParseInterval(argv
[2])))
6264 reply("MSG_INVALID_DURATION", argv
[2]);
6267 expiry
= now
+ duration
;
6270 target
->expires
= expiry
;
6273 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6275 target
->flags
|= USER_SUSPENDED
;
6276 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6280 static CHANSERV_FUNC(cmd_unsuspend
)
6282 struct handle_info
*hi
;
6283 struct userData
*self
, *target
;
6286 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6287 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6288 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6290 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6293 if(target
->access
>= self
->access
)
6295 reply("MSG_USER_OUTRANKED", hi
->handle
);
6298 if(!(target
->flags
& USER_SUSPENDED
))
6300 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6303 target
->flags
&= ~USER_SUSPENDED
;
6304 scan_user_presence(target
, NULL
);
6305 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6306 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6310 static MODCMD_FUNC(cmd_deleteme
)
6312 struct handle_info
*hi
;
6313 struct userData
*target
;
6314 const char *confirm_string
;
6315 unsigned short access
;
6318 hi
= user
->handle_info
;
6319 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6321 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6324 if(target
->access
== UL_OWNER
)
6326 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6329 confirm_string
= make_confirmation_string(target
);
6330 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6332 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6335 access
= target
->access
;
6336 channel_name
= strdup(channel
->name
);
6337 del_channel_user(target
, 1);
6338 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6344 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6346 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6347 struct chanData
*cData
;
6350 for(cData
= channelList
; cData
; cData
= cData
->next
)
6352 if(IsSuspended(cData
))
6354 opt
= cData
->chOpts
[chTopicRefresh
];
6357 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6360 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6361 cData
->last_refresh
= refresh_num
;
6363 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6366 static CHANSERV_FUNC(cmd_unf
)
6370 char response
[MAXLEN
];
6371 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6372 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6373 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6376 reply("CSMSG_UNF_RESPONSE");
6380 static CHANSERV_FUNC(cmd_ping
)
6384 char response
[MAXLEN
];
6385 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6386 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6387 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6390 reply("CSMSG_PING_RESPONSE");
6394 static CHANSERV_FUNC(cmd_wut
)
6398 char response
[MAXLEN
];
6399 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6400 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6401 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6404 reply("CSMSG_WUT_RESPONSE");
6409 static CHANSERV_FUNC(cmd_8ball
)
6411 unsigned int i
, j
, accum
;
6416 for(i
=1; i
<argc
; i
++)
6417 for(j
=0; argv
[i
][j
]; j
++)
6418 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6419 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6422 char response
[MAXLEN
];
6423 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6424 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6427 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6431 #else /* Use cool 8ball instead */
6433 void eightball(char *outcome
, int method
, unsigned int seed
)
6437 #define NUMOFCOLORS 18
6438 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6439 "white", "black", "grey", "brown",
6440 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6441 "fuchsia","turquoise","magenta", "cyan"};
6442 #define NUMOFLOCATIONS 50
6443 char balllocations
[50][55] = {
6444 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6445 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6446 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6447 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6448 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6449 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6450 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6451 "your bra", "your hair", "your bed", "the couch", "the wall",
6452 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6453 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6454 #define NUMOFPREPS 15
6455 char ballpreps
[50][50] = {
6456 "Near", "Somewhere near", "In", "In", "In",
6457 "In", "Hiding in", "Under", "Next to", "Over",
6458 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6459 #define NUMOFNUMS 34
6460 char ballnums
[50][50] = {
6461 "A hundred", "A thousand", "A few", "42",
6462 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6463 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6464 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6466 #define NUMOFMULTS 8
6467 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6470 * 0: normal (Not used in x3)
6477 if (method
== 1) /* A Color */
6481 answer
= (rand() % 12); /* Make sure this is the # of entries */
6484 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6486 case 1: strcpy(tmp
, "Sort of a light %s color.");
6488 case 2: strcpy(tmp
, "Dark and dreary %s.");
6490 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6492 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6494 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6496 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6498 case 10: strcpy(tmp
, "Solid %s.");
6500 case 11: strcpy(tmp
, "Transparent %s.");
6502 default: strcpy(outcome
, "An invalid random number was generated.");
6505 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6508 else if (method
== 2) /* Location */
6510 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6512 else if (method
== 3) /* Number of ___ */
6514 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6518 //Debug(DBGWARNING, "Error in 8ball.");
6523 static CHANSERV_FUNC(cmd_8ball
)
6525 char *word1
, *word2
, *word3
;
6526 static char eb
[MAXLEN
];
6527 unsigned int accum
, i
, j
;
6531 for(i
=1; i
<argc
; i
++)
6532 for(j
=0; argv
[i
][j
]; j
++)
6533 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6535 accum
+= time(NULL
)/3600;
6537 word2
= argc
>2?argv
[2]:"";
6538 word3
= argc
>3?argv
[3]:"";
6541 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6542 eightball(eb
, 1, accum
);
6543 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6544 eightball(eb
, 1, accum
);
6545 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6546 eightball(eb
, 1, accum
);
6547 /*** LOCATION *****/
6552 (strcasecmp(word1
, "where") == 0) &&
6553 (strcasecmp(word2
, "is") == 0)
6557 strcasecmp(word1
, "where's") == 0
6560 eightball(eb
, 2, accum
);
6562 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6563 eightball(eb
, 3, accum
);
6567 /* Generic 8ball question.. so pull from x3.conf srvx style */
6570 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6573 char response
[MAXLEN
];
6574 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6575 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6578 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6584 char response
[MAXLEN
];
6585 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6586 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6589 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6594 static CHANSERV_FUNC(cmd_d
)
6596 unsigned long sides
, count
, modifier
, ii
, total
;
6597 char response
[MAXLEN
], *sep
;
6601 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6611 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6612 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6616 else if((sep
[0] == '-') && isdigit(sep
[1]))
6617 modifier
= strtoul(sep
, NULL
, 10);
6618 else if((sep
[0] == '+') && isdigit(sep
[1]))
6619 modifier
= strtoul(sep
+1, NULL
, 10);
6626 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6631 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6634 for(total
= ii
= 0; ii
< count
; ++ii
)
6635 total
+= (rand() % sides
) + 1;
6638 if((count
> 1) || modifier
)
6640 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6641 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6645 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6646 sprintf(response
, fmt
, total
, sides
);
6649 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6651 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6655 static CHANSERV_FUNC(cmd_huggle
)
6657 /* CTCP must be via PRIVMSG, never notice */
6659 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6661 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6665 static CHANSERV_FUNC(cmd_calc
)
6667 char response
[MAXLEN
];
6670 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6673 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6675 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6680 chanserv_adjust_limit(void *data
)
6682 struct mod_chanmode change
;
6683 struct chanData
*cData
= data
;
6684 struct chanNode
*channel
= cData
->channel
;
6687 if(IsSuspended(cData
))
6690 cData
->limitAdjusted
= now
;
6691 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6692 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6694 if(limit
> cData
->modes
.new_limit
)
6695 limit
= cData
->modes
.new_limit
;
6696 else if(limit
== cData
->modes
.new_limit
)
6700 mod_chanmode_init(&change
);
6701 change
.modes_set
= MODE_LIMIT
;
6702 change
.new_limit
= limit
;
6703 mod_chanmode_announce(chanserv
, channel
, &change
);
6707 handle_new_channel(struct chanNode
*channel
)
6709 struct chanData
*cData
;
6711 if(!(cData
= channel
->channel_info
))
6714 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6715 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6717 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6718 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6721 /* Welcome to my worst nightmare. Warning: Read (or modify)
6722 the code below at your own risk. */
6724 handle_join(struct modeNode
*mNode
)
6726 struct mod_chanmode change
;
6727 struct userNode
*user
= mNode
->user
;
6728 struct chanNode
*channel
= mNode
->channel
;
6729 struct chanData
*cData
;
6730 struct userData
*uData
= NULL
;
6731 struct banData
*bData
;
6732 struct handle_info
*handle
;
6733 unsigned int modes
= 0, info
= 0;
6736 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6739 cData
= channel
->channel_info
;
6740 if(channel
->members
.used
> cData
->max
)
6741 cData
->max
= channel
->members
.used
;
6743 mod_chanmode_init(&change
);
6745 if(channel
->banlist
.used
< MAXBANS
)
6747 /* Not joining through a ban. */
6748 for(bData
= cData
->bans
;
6749 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6750 bData
= bData
->next
);
6754 char kick_reason
[MAXLEN
];
6755 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6757 bData
->triggered
= now
;
6758 if(bData
!= cData
->bans
)
6760 /* Shuffle the ban to the head of the list. */
6762 bData
->next
->prev
= bData
->prev
;
6764 bData
->prev
->next
= bData
->next
;
6767 bData
->next
= cData
->bans
;
6770 cData
->bans
->prev
= bData
;
6771 cData
->bans
= bData
;
6774 change
.args
[0].mode
= MODE_BAN
;
6775 change
.args
[0].u
.hostmask
= bData
->mask
;
6776 mod_chanmode_announce(chanserv
, channel
, &change
);
6777 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6782 /* ChanServ will not modify the limits in join-flooded channels.
6783 It will also skip DynLimit processing when the user (or srvx)
6784 is bursting in, because there are likely more incoming. */
6785 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6786 && !user
->uplink
->burst
6787 && !channel
->join_flooded
6788 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6790 /* The user count has begun "bumping" into the channel limit,
6791 so set a timer to raise the limit a bit. Any previous
6792 timers are removed so three incoming users within the delay
6793 results in one limit change, not three. */
6795 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6796 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6799 /* Give automodes exept during join-floods */
6800 if(!channel
->join_flooded
)
6802 if(cData
->chOpts
[chAutomode
] == 'v')
6803 modes
|= MODE_VOICE
;
6804 else if(cData
->chOpts
[chAutomode
] == 'h')
6805 modes
|= MODE_HALFOP
;
6806 else if(cData
->chOpts
[chAutomode
] == 'o')
6807 modes
|= MODE_CHANOP
;
6810 greeting
= cData
->greeting
;
6811 if(user
->handle_info
)
6813 handle
= user
->handle_info
;
6815 if(IsHelper(user
) && !IsHelping(user
))
6818 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6820 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6822 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6828 uData
= GetTrueChannelAccess(cData
, handle
);
6829 if(uData
&& !IsUserSuspended(uData
))
6831 /* non users getting automodes are handled above. */
6832 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
6834 if(uData
->access
>= UL_OP
)
6835 modes
|= MODE_CHANOP
;
6836 else if(uData
->access
>= UL_HALFOP
)
6837 modes
|= MODE_HALFOP
;
6838 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
6839 modes
|= MODE_VOICE
;
6841 if(uData
->access
>= UL_PRESENT
)
6842 cData
->visited
= now
;
6843 if(cData
->user_greeting
)
6844 greeting
= cData
->user_greeting
;
6846 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6847 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6854 if(!user
->uplink
->burst
)
6858 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
6859 if(modes & MODE_CHANOP) {
6860 modes &= ~MODE_HALFOP;
6861 modes &= ~MODE_VOICE;
6864 change
.args
[0].mode
= modes
;
6865 change
.args
[0].u
.member
= mNode
;
6866 mod_chanmode_announce(chanserv
, channel
, &change
);
6868 if(greeting
&& !user
->uplink
->burst
)
6869 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6871 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6877 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6879 struct mod_chanmode change
;
6880 struct userData
*channel
;
6881 unsigned int ii
, jj
;
6883 if(!user
->handle_info
)
6886 mod_chanmode_init(&change
);
6888 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6890 struct chanNode
*cn
;
6891 struct modeNode
*mn
;
6892 if(IsUserSuspended(channel
)
6893 || IsSuspended(channel
->channel
)
6894 || !(cn
= channel
->channel
->channel
))
6897 mn
= GetUserMode(cn
, user
);
6900 if(!IsUserSuspended(channel
)
6901 && IsUserAutoInvite(channel
)
6902 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6904 && !user
->uplink
->burst
)
6905 irc_invite(chanserv
, user
, cn
);
6909 if(channel
->access
>= UL_PRESENT
)
6910 channel
->channel
->visited
= now
;
6912 if(IsUserAutoOp(channel
))
6914 if(channel
->access
>= UL_OP
)
6915 change
.args
[0].mode
= MODE_CHANOP
;
6916 else if(channel
->access
>= UL_HALFOP
)
6917 change
.args
[0].mode
= MODE_HALFOP
;
6918 else if(channel
->access
>= UL_PEON
)
6919 change
.args
[0].mode
= MODE_VOICE
;
6921 change
.args
[0].mode
= 0;
6922 change
.args
[0].u
.member
= mn
;
6923 if(change
.args
[0].mode
)
6924 mod_chanmode_announce(chanserv
, cn
, &change
);
6927 channel
->seen
= now
;
6928 channel
->present
= 1;
6931 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6933 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6934 struct banData
*ban
;
6936 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6937 || !channel
->channel_info
6938 || IsSuspended(channel
->channel_info
))
6940 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6941 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6943 if(jj
< channel
->banlist
.used
)
6945 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6947 char kick_reason
[MAXLEN
];
6948 if(!user_matches_glob(user
, ban
->mask
, 1))
6950 change
.args
[0].mode
= MODE_BAN
;
6951 change
.args
[0].u
.hostmask
= ban
->mask
;
6952 mod_chanmode_announce(chanserv
, channel
, &change
);
6953 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6954 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6955 ban
->triggered
= now
;
6960 if(IsSupportHelper(user
))
6962 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6964 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6966 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6974 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6976 struct chanData
*cData
;
6977 struct userData
*uData
;
6979 cData
= mn
->channel
->channel_info
;
6980 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6983 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6985 /* Allow for a bit of padding so that the limit doesn't
6986 track the user count exactly, which could get annoying. */
6987 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6989 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6990 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6994 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6996 scan_user_presence(uData
, mn
->user
);
7000 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7002 unsigned int ii
, jj
;
7003 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7005 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7006 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7008 if(jj
< mn
->user
->channels
.used
)
7011 if(ii
== chanserv_conf
.support_channels
.used
)
7012 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7017 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7019 struct userData
*uData
;
7021 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7022 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7023 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7026 if(protect_user(victim
, kicker
, channel
->channel_info
))
7028 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7029 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7032 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7037 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7039 struct chanData
*cData
;
7041 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7044 cData
= channel
->channel_info
;
7045 if(bad_topic(channel
, user
, channel
->topic
))
7046 { /* User doesnt have privs to set topics. Undo it */
7047 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7048 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7051 /* If there is a topic mask set, and the new topic doesnt match,
7052 * set the topic to mask + new_topic */
7053 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7055 char new_topic
[TOPICLEN
+1];
7056 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7059 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7060 /* and fall through to topicsnarf code below.. */
7062 else /* Topic couldnt fit into mask, was too long */
7064 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7065 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7066 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7070 /* With topicsnarf, grab the topic and save it as the default topic. */
7071 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7074 cData
->topic
= strdup(channel
->topic
);
7080 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7082 struct mod_chanmode
*bounce
= NULL
;
7083 unsigned int bnc
, ii
;
7086 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7089 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7090 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7092 char correct
[MAXLEN
];
7093 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7094 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7095 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7097 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7099 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7101 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7102 if(!protect_user(victim
, user
, channel
->channel_info
))
7105 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7108 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7109 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7110 if(bounce
->args
[bnc
].u
.member
)
7114 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7115 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7117 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7119 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7121 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7122 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7125 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7126 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7127 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7130 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7132 const char *ban
= change
->args
[ii
].u
.hostmask
;
7133 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7136 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7137 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7138 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7140 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7145 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7146 mod_chanmode_announce(chanserv
, channel
, bounce
);
7147 for(ii
= 0; ii
< change
->argc
; ++ii
)
7148 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7149 free((char*)bounce
->args
[ii
].u
.hostmask
);
7150 mod_chanmode_free(bounce
);
7155 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7157 struct chanNode
*channel
;
7158 struct banData
*bData
;
7159 struct mod_chanmode change
;
7160 unsigned int ii
, jj
;
7161 char kick_reason
[MAXLEN
];
7163 mod_chanmode_init(&change
);
7165 change
.args
[0].mode
= MODE_BAN
;
7166 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7168 channel
= user
->channels
.list
[ii
]->channel
;
7169 /* Need not check for bans if they're opped or voiced. */
7170 /* TODO: does this make sense in automode v, h, and o? *
7171 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7172 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7174 /* Need not check for bans unless channel registration is active. */
7175 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7177 /* Look for a matching ban already on the channel. */
7178 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7179 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
7181 /* Need not act if we found one. */
7182 if(jj
< channel
->banlist
.used
)
7184 /* Look for a matching ban in this channel. */
7185 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7187 if(!user_matches_glob(user
, bData
->mask
, 1))
7189 change
.args
[0].u
.hostmask
= bData
->mask
;
7190 mod_chanmode_announce(chanserv
, channel
, &change
);
7191 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7192 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7193 bData
->triggered
= now
;
7194 break; /* we don't need to check any more bans in the channel */
7199 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7201 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7205 dict_remove2(handle_dnrs
, old_handle
, 1);
7206 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7207 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7212 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7214 struct userNode
*h_user
;
7216 if(handle
->channels
)
7218 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7219 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7221 while(handle
->channels
)
7222 del_channel_user(handle
->channels
, 1);
7227 handle_server_link(UNUSED_ARG(struct server
*server
))
7229 struct chanData
*cData
;
7231 for(cData
= channelList
; cData
; cData
= cData
->next
)
7233 if(!IsSuspended(cData
))
7234 cData
->may_opchan
= 1;
7235 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7236 && !cData
->channel
->join_flooded
7237 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7238 < chanserv_conf
.adjust_threshold
))
7240 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7241 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7247 chanserv_conf_read(void)
7251 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7252 struct mod_chanmode
*change
;
7253 struct string_list
*strlist
;
7254 struct chanNode
*chan
;
7257 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7259 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7262 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7263 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7264 chanserv_conf
.support_channels
.used
= 0;
7265 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7267 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7269 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7272 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7274 channelList_append(&chanserv_conf
.support_channels
, chan
);
7277 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7280 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7283 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7285 channelList_append(&chanserv_conf
.support_channels
, chan
);
7287 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7288 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7289 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7290 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7291 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7292 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7293 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7294 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7295 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7296 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7297 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7298 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7299 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7300 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7301 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7302 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7303 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7304 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7305 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7306 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7307 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7308 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7309 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7310 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7311 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7313 NickChange(chanserv
, str
, 0);
7314 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7315 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7316 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7317 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7318 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7319 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7320 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7321 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7322 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7323 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7324 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7325 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7326 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7327 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7328 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7329 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7330 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7333 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7334 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7335 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
7337 chanserv_conf
.default_modes
= *change
;
7338 mod_chanmode_free(change
);
7340 free_string_list(chanserv_conf
.set_shows
);
7341 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7343 strlist
= string_list_copy(strlist
);
7346 static const char *list
[] = {
7347 /* free form text */
7348 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7349 /* options based on user level */
7350 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7351 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7352 /* multiple choice options */
7353 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7354 /* binary options */
7355 "DynLimit", "NoDelete", "BanTimeout",
7360 strlist
= alloc_string_list(ArrayLength(list
)-1);
7361 for(ii
=0; list
[ii
]; ii
++)
7362 string_list_append(strlist
, strdup(list
[ii
]));
7364 chanserv_conf
.set_shows
= strlist
;
7365 /* We don't look things up now, in case the list refers to options
7366 * defined by modules initialized after this point. Just mark the
7367 * function list as invalid, so it will be initialized.
7369 set_shows_list
.used
= 0;
7370 free_string_list(chanserv_conf
.eightball
);
7371 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7374 strlist
= string_list_copy(strlist
);
7378 strlist
= alloc_string_list(4);
7379 string_list_append(strlist
, strdup("Yes."));
7380 string_list_append(strlist
, strdup("No."));
7381 string_list_append(strlist
, strdup("Maybe so."));
7383 chanserv_conf
.eightball
= strlist
;
7384 free_string_list(chanserv_conf
.old_ban_names
);
7385 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7387 strlist
= string_list_copy(strlist
);
7389 strlist
= alloc_string_list(2);
7390 chanserv_conf
.old_ban_names
= strlist
;
7391 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7392 off_channel
= str
? atoi(str
) : 0;
7396 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7399 struct note_type
*ntype
;
7402 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7404 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7407 if(!(ntype
= chanserv_create_note_type(key
)))
7409 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7413 /* Figure out set access */
7414 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7416 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7417 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7419 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7421 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7422 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7424 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7426 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7430 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7431 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7432 ntype
->set_access
.min_opserv
= 0;
7435 /* Figure out visibility */
7436 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7437 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7438 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7439 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7440 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7441 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7442 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7443 ntype
->visible_type
= NOTE_VIS_ALL
;
7445 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7447 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7448 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7452 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7454 struct handle_info
*handle
;
7455 struct userData
*uData
;
7456 char *seen
, *inf
, *flags
, *expires
;
7458 unsigned short access
;
7460 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7462 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7466 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7467 if(access
> UL_OWNER
)
7469 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7473 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7474 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7475 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7476 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7477 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7478 handle
= get_handle_info(key
);
7481 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7485 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7486 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7487 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7489 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7491 if(uData
->expires
> now
)
7492 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7494 uData
->flags
&= ~USER_SUSPENDED
;
7497 /* Upgrade: set autoop to the inverse of noautoop */
7498 if(chanserv_read_version
< 2)
7500 /* if noautoop is true, set autoop false, and vice versa */
7501 if(uData
->flags
& USER_NOAUTO_OP
)
7502 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7504 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7505 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
);
7511 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7513 struct banData
*bData
;
7514 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7515 time_t set_time
, triggered_time
, expires_time
;
7517 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7519 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7523 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7524 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7525 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7526 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7527 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7528 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7529 if (!reason
|| !owner
)
7532 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7533 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7535 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7537 expires_time
= set_time
+ atoi(s_duration
);
7541 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7544 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7547 static struct suspended
*
7548 chanserv_read_suspended(dict_t obj
)
7550 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7554 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7555 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7556 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7557 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7558 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7559 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7560 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7561 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7562 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7563 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7567 static struct giveownership
*
7568 chanserv_read_giveownership(dict_t obj
)
7570 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7574 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7575 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7577 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7579 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7580 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7582 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7583 giveownership
->reason
= str
? strdup(str
) : NULL
;
7584 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7585 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7587 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7588 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7589 return giveownership
;
7593 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7595 struct suspended
*suspended
;
7596 struct giveownership
*giveownership
;
7597 struct mod_chanmode
*modes
;
7598 struct chanNode
*cNode
;
7599 struct chanData
*cData
;
7600 struct dict
*channel
, *obj
;
7601 char *str
, *argv
[10];
7605 channel
= hir
->d
.object
;
7607 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7610 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7613 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7616 cData
= register_channel(cNode
, str
);
7619 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7623 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7625 enum levelOption lvlOpt
;
7626 enum charOption chOpt
;
7628 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7629 cData
->flags
= atoi(str
);
7631 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7633 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7635 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7636 else if(levelOptions
[lvlOpt
].old_flag
)
7638 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7639 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7641 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7645 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7647 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7649 cData
->chOpts
[chOpt
] = str
[0];
7652 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7654 enum levelOption lvlOpt
;
7655 enum charOption chOpt
;
7658 cData
->flags
= base64toint(str
, 5);
7659 count
= strlen(str
+= 5);
7660 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7663 if(levelOptions
[lvlOpt
].old_flag
)
7665 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7666 lvl
= levelOptions
[lvlOpt
].flag_value
;
7668 lvl
= levelOptions
[lvlOpt
].default_value
;
7670 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7672 case 'c': lvl
= UL_COOWNER
; break;
7673 case 'm': lvl
= UL_MANAGER
; break;
7674 case 'n': lvl
= UL_OWNER
+1; break;
7675 case 'o': lvl
= UL_OP
; break;
7676 case 'p': lvl
= UL_PEON
; break;
7677 case 'h': lvl
= UL_HALFOP
; break;
7678 case 'w': lvl
= UL_OWNER
; break;
7679 default: lvl
= 0; break;
7681 cData
->lvlOpts
[lvlOpt
] = lvl
;
7683 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7684 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7687 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7689 suspended
= chanserv_read_suspended(obj
);
7690 cData
->suspended
= suspended
;
7691 suspended
->cData
= cData
;
7692 /* We could use suspended->expires and suspended->revoked to
7693 * set the CHANNEL_SUSPENDED flag, but we don't. */
7695 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7697 suspended
= calloc(1, sizeof(*suspended
));
7698 suspended
->issued
= 0;
7699 suspended
->revoked
= 0;
7700 suspended
->suspender
= strdup(str
);
7701 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7702 suspended
->expires
= str
? atoi(str
) : 0;
7703 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7704 suspended
->reason
= strdup(str
? str
: "No reason");
7705 suspended
->previous
= NULL
;
7706 cData
->suspended
= suspended
;
7707 suspended
->cData
= cData
;
7711 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7712 suspended
= NULL
; /* to squelch a warning */
7715 if(IsSuspended(cData
)) {
7716 if(suspended
->expires
> now
)
7717 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7718 else if(suspended
->expires
)
7719 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7722 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
7724 giveownership
= chanserv_read_giveownership(obj
);
7725 cData
->giveownership
= giveownership
;
7728 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7729 struct mod_chanmode change
;
7730 mod_chanmode_init(&change
);
7732 change
.args
[0].mode
= MODE_CHANOP
;
7733 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7734 mod_chanmode_announce(chanserv
, cNode
, &change
);
7737 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7738 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7739 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7740 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7741 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7742 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7743 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7744 cData
->max
= str
? atoi(str
) : 0;
7745 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7746 cData
->greeting
= str
? strdup(str
) : NULL
;
7747 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7748 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7749 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7750 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7751 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7752 cData
->topic
= str
? strdup(str
) : NULL
;
7754 if(!IsSuspended(cData
)
7755 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7756 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7757 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7758 cData
->modes
= *modes
;
7760 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7761 if(cData
->modes
.argc
> 1)
7762 cData
->modes
.argc
= 1;
7763 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7764 mod_chanmode_free(modes
);
7767 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7768 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7769 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7771 if(!cData
->users
&& !IsProtected(cData
))
7773 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7774 unregister_channel(cData
, "has empty user list.");
7778 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7779 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7780 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7782 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7783 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7785 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7786 struct record_data
*rd
= iter_data(it
);
7787 const char *note
, *setter
;
7789 if(rd
->type
!= RECDB_OBJECT
)
7791 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7795 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7797 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7799 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7803 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7804 if(!setter
) setter
= "<unknown>";
7805 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7813 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7815 const char *setter
, *reason
, *str
;
7816 struct do_not_register
*dnr
;
7818 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7821 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7824 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7827 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7830 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7833 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7835 dnr
->set
= atoi(str
);
7841 chanserv_version_read(struct dict
*section
)
7845 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7847 chanserv_read_version
= atoi(str
);
7848 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7852 chanserv_saxdb_read(struct dict
*database
)
7854 struct dict
*section
;
7857 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7858 chanserv_version_read(section
);
7860 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7861 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7862 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7864 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7865 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7866 chanserv_channel_read(iter_key(it
), iter_data(it
));
7868 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7869 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7870 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7876 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7878 int high_present
= 0;
7879 saxdb_start_record(ctx
, KEY_USERS
, 1);
7880 for(; uData
; uData
= uData
->next
)
7882 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7884 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7885 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7886 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7888 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7890 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
7892 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7893 saxdb_end_record(ctx
);
7895 saxdb_end_record(ctx
);
7896 return high_present
;
7900 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7904 saxdb_start_record(ctx
, KEY_BANS
, 1);
7905 for(; bData
; bData
= bData
->next
)
7907 saxdb_start_record(ctx
, bData
->mask
, 0);
7908 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7909 if(bData
->triggered
)
7910 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7912 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7914 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7916 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7917 saxdb_end_record(ctx
);
7919 saxdb_end_record(ctx
);
7923 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7925 saxdb_start_record(ctx
, name
, 0);
7926 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7927 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7929 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7931 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7933 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7935 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7936 saxdb_end_record(ctx
);
7940 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
7942 saxdb_start_record(ctx
, name
, 0);
7943 if(giveownership
->staff_issuer
)
7944 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
7945 if(giveownership
->old_owner
)
7946 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
7947 if(giveownership
->target
)
7948 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
7949 if(giveownership
->target_access
)
7950 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
7951 if(giveownership
->reason
)
7952 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
7953 if(giveownership
->issued
)
7954 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
7955 if(giveownership
->previous
)
7956 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
7957 saxdb_end_record(ctx
);
7961 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7965 enum levelOption lvlOpt
;
7966 enum charOption chOpt
;
7968 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7970 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7971 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7973 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7974 if(channel
->registrar
)
7975 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7976 if(channel
->greeting
)
7977 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7978 if(channel
->user_greeting
)
7979 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7980 if(channel
->topic_mask
)
7981 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7982 if(channel
->suspended
)
7983 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7984 if(channel
->giveownership
)
7985 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
7987 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7988 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7989 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7990 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7991 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7993 buf
[0] = channel
->chOpts
[chOpt
];
7995 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7997 saxdb_end_record(ctx
);
7999 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8001 mod_chanmode_format(&channel
->modes
, buf
);
8002 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8005 high_present
= chanserv_write_users(ctx
, channel
->users
);
8006 chanserv_write_bans(ctx
, channel
->bans
);
8008 if(dict_size(channel
->notes
))
8012 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8013 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8015 struct note
*note
= iter_data(it
);
8016 saxdb_start_record(ctx
, iter_key(it
), 0);
8017 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8018 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8019 saxdb_end_record(ctx
);
8021 saxdb_end_record(ctx
);
8024 if(channel
->ownerTransfer
)
8025 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8026 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8027 saxdb_end_record(ctx
);
8031 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8035 saxdb_start_record(ctx
, ntype
->name
, 0);
8036 switch(ntype
->set_access_type
)
8038 case NOTE_SET_CHANNEL_ACCESS
:
8039 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8041 case NOTE_SET_CHANNEL_SETTER
:
8042 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8044 case NOTE_SET_PRIVILEGED
: default:
8045 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8048 switch(ntype
->visible_type
)
8050 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8051 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8052 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8054 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8055 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8056 saxdb_end_record(ctx
);
8060 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8062 struct do_not_register
*dnr
;
8065 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8067 dnr
= iter_data(it
);
8068 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8070 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8071 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8072 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8073 saxdb_end_record(ctx
);
8078 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8081 struct chanData
*channel
;
8083 /* Version Control*/
8084 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8085 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8086 saxdb_end_record(ctx
);
8089 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8090 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8091 chanserv_write_note_type(ctx
, iter_data(it
));
8092 saxdb_end_record(ctx
);
8095 saxdb_start_record(ctx
, KEY_DNR
, 1);
8096 write_dnrs_helper(ctx
, handle_dnrs
);
8097 write_dnrs_helper(ctx
, plain_dnrs
);
8098 write_dnrs_helper(ctx
, mask_dnrs
);
8099 saxdb_end_record(ctx
);
8102 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8103 for(channel
= channelList
; channel
; channel
= channel
->next
)
8104 chanserv_write_channel(ctx
, channel
);
8105 saxdb_end_record(ctx
);
8111 chanserv_db_cleanup(void) {
8113 unreg_part_func(handle_part
);
8115 unregister_channel(channelList
, "terminating.");
8116 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8117 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8118 free(chanserv_conf
.support_channels
.list
);
8119 dict_delete(handle_dnrs
);
8120 dict_delete(plain_dnrs
);
8121 dict_delete(mask_dnrs
);
8122 dict_delete(note_types
);
8123 free_string_list(chanserv_conf
.eightball
);
8124 free_string_list(chanserv_conf
.old_ban_names
);
8125 free_string_list(chanserv_conf
.set_shows
);
8126 free(set_shows_list
.list
);
8127 free(uset_shows_list
.list
);
8130 struct userData
*helper
= helperList
;
8131 helperList
= helperList
->next
;
8136 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8137 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8138 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8141 init_chanserv(const char *nick
)
8143 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8144 conf_register_reload(chanserv_conf_read
);
8146 reg_server_link_func(handle_server_link
);
8148 reg_new_channel_func(handle_new_channel
);
8149 reg_join_func(handle_join
);
8150 reg_part_func(handle_part
);
8151 reg_kick_func(handle_kick
);
8152 reg_topic_func(handle_topic
);
8153 reg_mode_change_func(handle_mode
);
8154 reg_nick_change_func(handle_nick_change
);
8156 reg_auth_func(handle_auth
);
8157 reg_handle_rename_func(handle_rename
);
8158 reg_unreg_func(handle_unreg
);
8160 handle_dnrs
= dict_new();
8161 dict_set_free_data(handle_dnrs
, free
);
8162 plain_dnrs
= dict_new();
8163 dict_set_free_data(plain_dnrs
, free
);
8164 mask_dnrs
= dict_new();
8165 dict_set_free_data(mask_dnrs
, free
);
8167 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8168 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8169 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8170 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8171 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8172 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8173 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8174 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8175 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8176 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8178 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8180 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8181 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8183 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8184 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8185 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8186 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8187 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8189 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8190 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8191 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8192 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8193 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8194 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8196 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8197 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8198 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8199 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8201 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8202 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8203 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8204 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8205 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8206 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8207 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8208 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8209 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8210 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8212 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8213 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8214 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8215 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8216 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8217 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8218 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8219 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8220 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8221 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8222 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8223 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8224 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8225 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8227 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8228 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8229 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8230 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8232 /* if you change dellamer access, see also places
8233 * like unbanme which have manager hardcoded. */
8234 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8235 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8237 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8239 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8241 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8242 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8243 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8244 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8245 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8246 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8247 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8248 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8249 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8250 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8251 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8252 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8254 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8255 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8257 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8258 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8259 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8260 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8262 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8263 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8264 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8265 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8266 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8268 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8269 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8270 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8271 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8272 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8273 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8274 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8276 /* Channel options */
8277 DEFINE_CHANNEL_OPTION(defaulttopic
);
8278 DEFINE_CHANNEL_OPTION(topicmask
);
8279 DEFINE_CHANNEL_OPTION(greeting
);
8280 DEFINE_CHANNEL_OPTION(usergreeting
);
8281 DEFINE_CHANNEL_OPTION(modes
);
8282 DEFINE_CHANNEL_OPTION(enfops
);
8283 DEFINE_CHANNEL_OPTION(enfhalfops
);
8284 DEFINE_CHANNEL_OPTION(automode
);
8285 DEFINE_CHANNEL_OPTION(protect
);
8286 DEFINE_CHANNEL_OPTION(enfmodes
);
8287 DEFINE_CHANNEL_OPTION(enftopic
);
8288 DEFINE_CHANNEL_OPTION(pubcmd
);
8289 DEFINE_CHANNEL_OPTION(userinfo
);
8290 DEFINE_CHANNEL_OPTION(dynlimit
);
8291 DEFINE_CHANNEL_OPTION(topicsnarf
);
8292 DEFINE_CHANNEL_OPTION(nodelete
);
8293 DEFINE_CHANNEL_OPTION(toys
);
8294 DEFINE_CHANNEL_OPTION(setters
);
8295 DEFINE_CHANNEL_OPTION(topicrefresh
);
8296 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8297 DEFINE_CHANNEL_OPTION(bantimeout
);
8298 DEFINE_CHANNEL_OPTION(inviteme
);
8300 DEFINE_CHANNEL_OPTION(offchannel
);
8301 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8303 /* Alias set topic to set defaulttopic for compatibility. */
8304 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8307 DEFINE_USER_OPTION(autoinvite
);
8308 DEFINE_USER_OPTION(info
);
8309 DEFINE_USER_OPTION(autoop
);
8311 /* Alias uset autovoice to uset autoop. */
8312 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8314 note_types
= dict_new();
8315 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8318 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8319 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8320 service_register(chanserv
)->trigger
= '!';
8321 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8324 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8326 if(chanserv_conf
.channel_expire_frequency
)
8327 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8329 if(chanserv_conf
.ban_timeout_frequency
)
8330 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8332 if(chanserv_conf
.refresh_period
)
8334 time_t next_refresh
;
8335 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8336 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8339 reg_exit_func(chanserv_db_cleanup
);
8340 message_register_table(msgtab
);