1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
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_NORMAL", "$b%s Users From Level %s To %s$b" },
358 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
359 /* uncomment if needed to adujust styles (and change code below)
360 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
361 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
362 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
363 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
364 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
365 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
367 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
368 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
369 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
371 /* Channel note list */
372 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
373 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
374 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
375 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
376 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
377 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
378 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
379 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
380 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
381 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
382 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
383 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
384 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
385 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
386 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
388 /* Channel [un]suspension */
389 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
390 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
391 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
392 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
393 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
394 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
395 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
397 /* Access information */
398 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
399 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
400 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
401 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
402 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
403 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
404 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
405 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
406 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
407 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
408 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
410 /* Seen information */
411 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
412 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
413 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
414 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
416 /* Names information */
417 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
418 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
420 /* Channel information */
421 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
422 { "CSMSG_BAR", "----------------------------------------"},
423 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
424 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
425 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
426 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
427 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
428 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
429 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
430 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
431 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
432 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
433 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
435 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
436 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
437 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
438 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
439 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
440 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
441 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
442 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
443 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
444 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
445 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
446 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
447 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
448 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
450 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
451 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
452 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
453 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
454 { "CSMSG_PEEK_OPS", "$bOps:$b" },
455 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
456 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
458 /* Network information */
459 { "CSMSG_NETWORK_INFO", "Network Information:" },
460 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
461 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
462 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
463 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
464 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
465 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
466 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
467 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
470 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
471 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
472 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
474 /* Channel searches */
475 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
476 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
477 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
478 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
480 /* Channel configuration */
481 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
482 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
483 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
484 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
487 { "CSMSG_USER_OPTIONS", "User Options:" },
488 // { "CSMSG_USER_PROTECTED", "That user is protected." },
491 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
492 { "CSMSG_PING_RESPONSE", "Pong!" },
493 { "CSMSG_WUT_RESPONSE", "wut" },
494 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
495 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
496 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
497 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
498 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
499 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
500 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
503 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
504 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
508 /* eject_user and unban_user flags */
509 #define ACTION_KICK 0x0001
510 #define ACTION_BAN 0x0002
511 #define ACTION_ADD_LAMER 0x0004
512 #define ACTION_ADD_TIMED_LAMER 0x0008
513 #define ACTION_UNBAN 0x0010
514 #define ACTION_DEL_LAMER 0x0020
516 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
517 #define MODELEN 40 + KEYLEN
521 #define CSFUNC_ARGS user, channel, argc, argv, cmd
523 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
524 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
525 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
526 reply("MSG_MISSING_PARAMS", argv[0]); \
530 DECLARE_LIST(dnrList
, struct do_not_register
*);
531 DEFINE_LIST(dnrList
, struct do_not_register
*);
533 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
535 struct userNode
*chanserv
;
538 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
539 static struct log_type
*CS_LOG
;
540 struct adduserPending
* adduser_pendings
= NULL
;
541 unsigned int adduser_pendings_count
= 0;
545 struct channelList support_channels
;
546 struct mod_chanmode default_modes
;
548 unsigned long db_backup_frequency
;
549 unsigned long channel_expire_frequency
;
550 unsigned long ban_timeout_frequency
;
553 unsigned int adjust_delay
;
554 long channel_expire_delay
;
555 unsigned int nodelete_level
;
557 unsigned int adjust_threshold
;
558 int join_flood_threshold
;
560 unsigned int greeting_length
;
561 unsigned int refresh_period
;
562 unsigned int giveownership_period
;
564 unsigned int max_owned
;
565 unsigned int max_chan_users
;
566 unsigned int max_chan_bans
; /* lamers */
567 unsigned int max_userinfo_length
;
569 struct string_list
*set_shows
;
570 struct string_list
*eightball
;
571 struct string_list
*old_ban_names
;
573 const char *ctcp_short_ban_duration
;
574 const char *ctcp_long_ban_duration
;
576 const char *irc_operator_epithet
;
577 const char *network_helper_epithet
;
578 const char *support_helper_epithet
;
583 struct userNode
*user
;
584 struct userNode
*bot
;
585 struct chanNode
*channel
;
587 unsigned short lowest
;
588 unsigned short highest
;
589 struct userData
**users
;
590 struct helpfile_table table
;
593 enum note_access_type
595 NOTE_SET_CHANNEL_ACCESS
,
596 NOTE_SET_CHANNEL_SETTER
,
600 enum note_visible_type
603 NOTE_VIS_CHANNEL_USERS
,
609 enum note_access_type set_access_type
;
611 unsigned int min_opserv
;
612 unsigned short min_ulevel
;
614 enum note_visible_type visible_type
;
615 unsigned int max_length
;
622 struct note_type
*type
;
623 char setter
[NICKSERV_HANDLE_LEN
+1];
627 static unsigned int registered_channels
;
628 static unsigned int banCount
;
630 static const struct {
633 unsigned short level
;
635 } accessLevels
[] = { /* MUST be orderd less to most! */
636 { "peon", "Peon", UL_PEON
, '+' },
637 { "halfop", "HalfOp", UL_HALFOP
, '%' },
638 { "op", "Op", UL_OP
, '@' },
639 { "manager", "Manager", UL_MANAGER
, '%' },
640 { "coowner", "Coowner", UL_COOWNER
, '*' },
641 { "owner", "Owner", UL_OWNER
, '!' },
642 { "helper", "BUG:", UL_HELPER
, 'X' }
645 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
646 static const struct {
649 unsigned short default_value
;
650 unsigned int old_idx
;
651 unsigned int old_flag
;
652 unsigned short flag_value
;
654 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
655 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
656 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
657 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
658 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
659 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
660 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
661 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
662 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
665 struct charOptionValues
{
668 } automodeValues
[] = {
669 { 'n', "CSMSG_AUTOMODE_NONE" },
670 { 'y', "CSMSG_AUTOMODE_NORMAL" },
671 { 'v', "CSMSG_AUTOMODE_VOICE" },
672 { 'h', "CSMSG_AUTOMODE_HOP" },
673 { 'o', "CSMSG_AUTOMODE_OP" },
674 { 'm', "CSMSG_AUTOMODE_MUTE" }
675 }, protectValues
[] = {
676 { 'a', "CSMSG_PROTECT_ALL" },
677 { 'e', "CSMSG_PROTECT_EQUAL" },
678 { 'l', "CSMSG_PROTECT_LOWER" },
679 { 'n', "CSMSG_PROTECT_NONE" }
681 { 'd', "CSMSG_TOYS_DISABLED" },
682 { 'n', "CSMSG_TOYS_PRIVATE" },
683 { 'p', "CSMSG_TOYS_PUBLIC" }
684 }, topicRefreshValues
[] = {
685 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
686 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
687 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
688 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
689 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
690 }, ctcpReactionValues
[] = {
691 { 'n', "CSMSG_CTCPREACTION_NONE" },
692 { 'k', "CSMSG_CTCPREACTION_KICK" },
693 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
694 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
695 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
696 }, banTimeoutValues
[] = {
697 { '0', "CSMSG_BANTIMEOUT_NONE" },
698 { '1', "CSMSG_BANTIMEOUT_10M" },
699 { '2', "CSMSG_BANTIMEOUT_2H" },
700 { '3', "CSMSG_BANTIMEOUT_4H" },
701 { '4', "CSMSG_BANTIMEOUT_1D" },
702 { '5', "CSMSG_BANTIMEOUT_1W" }
705 static const struct {
709 unsigned int old_idx
;
711 struct charOptionValues
*values
;
713 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
714 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
715 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
716 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
717 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
718 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
}
721 struct userData
*helperList
;
722 struct chanData
*channelList
;
723 static struct module *chanserv_module
;
724 static unsigned int userCount
;
725 unsigned int chanserv_read_version
= 0; /* db version control */
727 #define CHANSERV_DB_VERSION 2
729 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
730 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
731 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
734 user_level_from_name(const char *name
, unsigned short clamp_level
)
736 unsigned int level
= 0, ii
;
738 level
= strtoul(name
, NULL
, 10);
739 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
740 if(!irccasecmp(name
, accessLevels
[ii
].name
))
741 level
= accessLevels
[ii
].level
;
742 if(level
> clamp_level
)
748 user_level_name_from_level(int level
)
756 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
757 if(level
>= accessLevels
[ii
].level
)
758 highest
= accessLevels
[ii
].title
;
764 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
767 *minl
= strtoul(arg
, &sep
, 10);
775 *maxl
= strtoul(sep
+1, &sep
, 10);
783 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
785 struct userData
*uData
, **head
;
787 if(!channel
|| !handle
)
790 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
791 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
793 for(uData
= helperList
;
794 uData
&& uData
->handle
!= handle
;
795 uData
= uData
->next
);
799 uData
= calloc(1, sizeof(struct userData
));
800 uData
->handle
= handle
;
802 uData
->access
= UL_HELPER
;
808 uData
->next
= helperList
;
810 helperList
->prev
= uData
;
818 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
819 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
822 head
= &(channel
->users
);
825 if(uData
&& (uData
!= *head
))
827 /* Shuffle the user to the head of whatever list he was in. */
829 uData
->next
->prev
= uData
->prev
;
831 uData
->prev
->next
= uData
->next
;
837 (**head
).prev
= uData
;
844 /* Returns non-zero if user has at least the minimum access.
845 * exempt_owner is set when handling !set, so the owner can set things
848 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
850 struct userData
*uData
;
851 struct chanData
*cData
= channel
->channel_info
;
852 unsigned short minimum
= cData
->lvlOpts
[opt
];
855 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
858 if(minimum
<= uData
->access
)
860 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
865 /* Scan for other users authenticated to the same handle
866 still in the channel. If so, keep them listed as present.
868 user is optional, if not null, it skips checking that userNode
869 (for the handle_part function) */
871 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
875 if(IsSuspended(uData
->channel
)
876 || IsUserSuspended(uData
)
877 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
889 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
891 unsigned int eflags
, argc
;
893 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
895 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
896 if(!channel
->channel_info
897 || IsSuspended(channel
->channel_info
)
899 || !ircncasecmp(text
, "ACTION ", 7))
901 /* We dont punish people we know -Rubin
902 * * Figure out the minimum level needed to CTCP the channel *
904 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
907 /* If they are a user of the channel, they are exempt */
908 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
910 /* We need to enforce against them; do so. */
913 argv
[1] = user
->nick
;
915 if(GetUserMode(channel
, user
))
916 eflags
|= ACTION_KICK
;
917 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
918 default: case 'n': return;
920 eflags
|= ACTION_KICK
;
923 eflags
|= ACTION_BAN
;
926 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
927 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
930 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
931 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
934 argv
[argc
++] = bad_ctcp_reason
;
935 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
939 chanserv_create_note_type(const char *name
)
941 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
942 strcpy(ntype
->name
, name
);
944 dict_insert(note_types
, ntype
->name
, ntype
);
949 chanserv_deref_note_type(void *data
)
951 struct note_type
*ntype
= data
;
953 if(--ntype
->refs
> 0)
959 chanserv_flush_note_type(struct note_type
*ntype
)
961 struct chanData
*cData
;
962 for(cData
= channelList
; cData
; cData
= cData
->next
)
963 dict_remove(cData
->notes
, ntype
->name
);
967 chanserv_truncate_notes(struct note_type
*ntype
)
969 struct chanData
*cData
;
971 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
973 for(cData
= channelList
; cData
; cData
= cData
->next
) {
974 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
977 if(strlen(note
->note
) <= ntype
->max_length
)
979 dict_remove2(cData
->notes
, ntype
->name
, 1);
980 note
= realloc(note
, size
);
981 note
->note
[ntype
->max_length
] = 0;
982 dict_insert(cData
->notes
, ntype
->name
, note
);
986 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
989 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
992 unsigned int len
= strlen(text
);
994 if(len
> type
->max_length
) len
= type
->max_length
;
995 note
= calloc(1, sizeof(*note
) + len
);
997 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
998 memcpy(note
->note
, text
, len
);
1000 dict_insert(channel
->notes
, type
->name
, note
);
1006 chanserv_free_note(void *data
)
1008 struct note
*note
= data
;
1010 chanserv_deref_note_type(note
->type
);
1011 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1015 static MODCMD_FUNC(cmd_createnote
) {
1016 struct note_type
*ntype
;
1017 unsigned int arg
= 1, existed
= 0, max_length
;
1019 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1022 ntype
= chanserv_create_note_type(argv
[arg
]);
1023 if(!irccasecmp(argv
[++arg
], "privileged"))
1026 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1027 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1029 else if(!irccasecmp(argv
[arg
], "channel"))
1031 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1034 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1037 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1038 ntype
->set_access
.min_ulevel
= ulvl
;
1040 else if(!irccasecmp(argv
[arg
], "setter"))
1042 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1046 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1050 if(!irccasecmp(argv
[++arg
], "privileged"))
1051 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1052 else if(!irccasecmp(argv
[arg
], "channel_users"))
1053 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1054 else if(!irccasecmp(argv
[arg
], "all"))
1055 ntype
->visible_type
= NOTE_VIS_ALL
;
1057 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1061 if((arg
+1) >= argc
) {
1062 reply("MSG_MISSING_PARAMS", argv
[0]);
1065 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1066 if(max_length
< 20 || max_length
> 450)
1068 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1071 if(existed
&& (max_length
< ntype
->max_length
))
1073 ntype
->max_length
= max_length
;
1074 chanserv_truncate_notes(ntype
);
1076 ntype
->max_length
= max_length
;
1079 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1081 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1086 dict_remove(note_types
, ntype
->name
);
1090 static MODCMD_FUNC(cmd_removenote
) {
1091 struct note_type
*ntype
;
1094 ntype
= dict_find(note_types
, argv
[1], NULL
);
1095 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1098 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1105 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1108 chanserv_flush_note_type(ntype
);
1110 dict_remove(note_types
, argv
[1]);
1111 reply("CSMSG_NOTE_DELETED", argv
[1]);
1116 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1120 if(orig
->modes_set
& change
->modes_clear
)
1122 if(orig
->modes_clear
& change
->modes_set
)
1124 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1125 && strcmp(orig
->new_key
, change
->new_key
))
1127 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1128 && (orig
->new_limit
!= change
->new_limit
))
1133 static char max_length_text
[MAXLEN
+1][16];
1135 static struct helpfile_expansion
1136 chanserv_expand_variable(const char *variable
)
1138 struct helpfile_expansion exp
;
1140 if(!irccasecmp(variable
, "notes"))
1143 exp
.type
= HF_TABLE
;
1144 exp
.value
.table
.length
= 1;
1145 exp
.value
.table
.width
= 3;
1146 exp
.value
.table
.flags
= 0;
1147 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1148 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1149 exp
.value
.table
.contents
[0][0] = "Note Type";
1150 exp
.value
.table
.contents
[0][1] = "Visibility";
1151 exp
.value
.table
.contents
[0][2] = "Max Length";
1152 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1154 struct note_type
*ntype
= iter_data(it
);
1157 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1158 row
= exp
.value
.table
.length
++;
1159 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1160 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1161 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1162 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1164 if(!max_length_text
[ntype
->max_length
][0])
1165 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1166 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1171 exp
.type
= HF_STRING
;
1172 exp
.value
.str
= NULL
;
1176 static struct chanData
*
1177 register_channel(struct chanNode
*cNode
, char *registrar
)
1179 struct chanData
*channel
;
1180 enum levelOption lvlOpt
;
1181 enum charOption chOpt
;
1183 channel
= calloc(1, sizeof(struct chanData
));
1185 channel
->notes
= dict_new();
1186 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1188 channel
->registrar
= strdup(registrar
);
1189 channel
->registered
= now
;
1190 channel
->visited
= now
;
1191 channel
->limitAdjusted
= now
;
1192 channel
->ownerTransfer
= now
;
1193 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1194 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1195 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1196 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1197 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1199 channel
->prev
= NULL
;
1200 channel
->next
= channelList
;
1203 channelList
->prev
= channel
;
1204 channelList
= channel
;
1205 registered_channels
++;
1207 channel
->channel
= cNode
;
1209 cNode
->channel_info
= channel
;
1214 static struct userData
*
1215 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1217 struct userData
*ud
;
1219 if(access
> UL_OWNER
)
1222 ud
= calloc(1, sizeof(*ud
));
1223 ud
->channel
= channel
;
1224 ud
->handle
= handle
;
1226 ud
->access
= access
;
1227 ud
->info
= info
? strdup(info
) : NULL
;
1230 ud
->next
= channel
->users
;
1232 channel
->users
->prev
= ud
;
1233 channel
->users
= ud
;
1235 channel
->userCount
++;
1239 ud
->u_next
= ud
->handle
->channels
;
1241 ud
->u_next
->u_prev
= ud
;
1242 ud
->handle
->channels
= ud
;
1244 ud
->flags
= USER_FLAGS_DEFAULT
;
1248 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1251 del_channel_user(struct userData
*user
, int do_gc
)
1253 struct chanData
*channel
= user
->channel
;
1255 channel
->userCount
--;
1259 user
->prev
->next
= user
->next
;
1261 channel
->users
= user
->next
;
1263 user
->next
->prev
= user
->prev
;
1266 user
->u_prev
->u_next
= user
->u_next
;
1268 user
->handle
->channels
= user
->u_next
;
1270 user
->u_next
->u_prev
= user
->u_prev
;
1274 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1275 unregister_channel(channel
, "lost all users.");
1278 static struct adduserPending
*
1279 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1281 struct adduserPending
*ap
;
1282 ap
= calloc(1,sizeof(struct adduserPending
));
1283 ap
->channel
= channel
;
1286 ap
->created
= time(NULL
);
1288 /* ap->prev defaults to NULL already.. */
1289 ap
->next
= adduser_pendings
;
1290 if(adduser_pendings
)
1291 adduser_pendings
->prev
= ap
;
1292 adduser_pendings
= ap
;
1293 adduser_pendings_count
++;
1298 del_adduser_pending(struct adduserPending
*ap
)
1301 ap
->prev
->next
= ap
->next
;
1303 adduser_pendings
= ap
->next
;
1306 ap
->next
->prev
= ap
->prev
;
1310 static void expire_adduser_pending();
1312 /* find_adduser_pending(channel, user) will find an arbitrary record
1313 * from user, channel, or user and channel.
1314 * if user or channel are NULL, they will match any records.
1316 static struct adduserPending
*
1317 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1319 struct adduserPending
*ap
;
1321 expire_adduser_pending(); /* why not here.. */
1323 if(!channel
&& !user
) /* 2 nulls matches all */
1324 return(adduser_pendings
);
1325 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1327 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1334 /* Remove all pendings for a user or channel
1336 * called in nickserv.c DelUser() and proto-* unregister_channel()
1339 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1341 struct adduserPending
*ap
;
1343 /* So this is a bit wastefull, i hate dealing with linked lists.
1344 * if its a problem we'll rewrite it right */
1345 while((ap
= find_adduser_pending(channel
, user
))) {
1346 del_adduser_pending(ap
);
1350 /* Called from nickserv.c cmd_auth after someone auths */
1352 process_adduser_pending(struct userNode
*user
)
1354 struct adduserPending
*ap
;
1355 if(!user
->handle_info
)
1356 return; /* not associated with an account */
1357 while((ap
= find_adduser_pending(NULL
, user
)))
1359 struct userData
*actee
;
1360 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1362 /* Already on the userlist. do nothing*/
1366 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1367 scan_user_presence(actee
, NULL
);
1369 del_adduser_pending(ap
);
1374 expire_adduser_pending()
1376 struct adduserPending
*ap
, *ap_next
;
1377 ap
= adduser_pendings
;
1380 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1382 ap_next
= ap
->next
; /* save next */
1383 del_adduser_pending(ap
); /* free and relink */
1384 ap
= ap_next
; /* advance */
1391 static void expire_ban(void *data
);
1393 static struct banData
*
1394 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1397 unsigned int ii
, l1
, l2
;
1402 bd
= malloc(sizeof(struct banData
));
1404 bd
->channel
= channel
;
1406 bd
->triggered
= triggered
;
1407 bd
->expires
= expires
;
1409 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1411 extern const char *hidden_host_suffix
;
1412 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1416 l2
= strlen(old_name
);
1419 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1421 new_mask
= alloca(MAXLEN
);
1422 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1425 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1427 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1428 bd
->reason
= strdup(reason
);
1431 timeq_add(expires
, expire_ban
, bd
);
1434 bd
->next
= channel
->bans
; /* lamers */
1436 channel
->bans
->prev
= bd
;
1438 channel
->banCount
++;
1445 del_channel_ban(struct banData
*ban
)
1447 ban
->channel
->banCount
--;
1451 ban
->prev
->next
= ban
->next
;
1453 ban
->channel
->bans
= ban
->next
;
1456 ban
->next
->prev
= ban
->prev
;
1459 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1468 expire_ban(void *data
) /* lamer.. */
1470 struct banData
*bd
= data
;
1471 if(!IsSuspended(bd
->channel
))
1473 struct banList bans
;
1474 struct mod_chanmode change
;
1476 bans
= bd
->channel
->channel
->banlist
;
1477 mod_chanmode_init(&change
);
1478 for(ii
=0; ii
<bans
.used
; ii
++)
1480 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1483 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1484 change
.args
[0].u
.hostmask
= bd
->mask
;
1485 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1491 del_channel_ban(bd
);
1494 static void chanserv_expire_suspension(void *data
);
1497 unregister_channel(struct chanData
*channel
, const char *reason
)
1499 struct mod_chanmode change
;
1500 char msgbuf
[MAXLEN
];
1502 /* After channel unregistration, the following must be cleaned
1504 - Channel information.
1506 - Channel bans. (lamers)
1507 - Channel suspension data.
1508 - adduser_pending data.
1509 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1515 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1519 mod_chanmode_init(&change
);
1520 change
.modes_clear
|= MODE_REGISTERED
;
1521 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1524 wipe_adduser_pending(channel
->channel
, NULL
);
1526 while(channel
->users
)
1527 del_channel_user(channel
->users
, 0);
1529 while(channel
->bans
)
1530 del_channel_ban(channel
->bans
);
1532 free(channel
->topic
);
1533 free(channel
->registrar
);
1534 free(channel
->greeting
);
1535 free(channel
->user_greeting
);
1536 free(channel
->topic_mask
);
1539 channel
->prev
->next
= channel
->next
;
1541 channelList
= channel
->next
;
1544 channel
->next
->prev
= channel
->prev
;
1546 if(channel
->suspended
)
1548 struct chanNode
*cNode
= channel
->channel
;
1549 struct suspended
*suspended
, *next_suspended
;
1551 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1553 next_suspended
= suspended
->previous
;
1554 free(suspended
->suspender
);
1555 free(suspended
->reason
);
1556 if(suspended
->expires
)
1557 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1562 cNode
->channel_info
= NULL
;
1564 channel
->channel
->channel_info
= NULL
;
1566 dict_delete(channel
->notes
);
1567 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1568 if(!IsSuspended(channel
))
1569 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1570 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1571 UnlockChannel(channel
->channel
);
1573 registered_channels
--;
1577 expire_channels(UNUSED_ARG(void *data
))
1579 struct chanData
*channel
, *next
;
1580 struct userData
*user
;
1581 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1583 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1584 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1586 for(channel
= channelList
; channel
; channel
= next
)
1588 next
= channel
->next
;
1590 /* See if the channel can be expired. */
1591 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1592 || IsProtected(channel
))
1595 /* Make sure there are no high-ranking users still in the channel. */
1596 for(user
=channel
->users
; user
; user
=user
->next
)
1597 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1602 /* Unregister the channel */
1603 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1604 unregister_channel(channel
, "registration expired.");
1607 if(chanserv_conf
.channel_expire_frequency
)
1608 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1612 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1614 char protect
= channel
->chOpts
[chProtect
];
1615 struct userData
*cs_victim
, *cs_aggressor
;
1617 /* Don't protect if no one is to be protected, someone is attacking
1618 himself, or if the aggressor is an IRC Operator. */
1619 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1622 /* Don't protect if the victim isn't authenticated (because they
1623 can't be a channel user), unless we are to protect non-users
1625 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1626 if(protect
!= 'a' && !cs_victim
)
1629 /* Protect if the aggressor isn't a user because at this point,
1630 the aggressor can only be less than or equal to the victim. */
1631 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1635 /* If the aggressor was a user, then the victim can't be helped. */
1642 if(cs_victim
->access
> cs_aggressor
->access
)
1647 if(cs_victim
->access
>= cs_aggressor
->access
)
1656 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1658 struct chanData
*cData
= channel
->channel_info
;
1659 struct userData
*cs_victim
;
1661 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1662 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1663 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1665 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1673 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1675 struct chanData
*cData
= channel
->channel_info
;
1676 struct userData
*cs_victim
;
1678 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1679 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1680 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1682 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1691 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1693 if(IsService(victim
))
1695 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1699 if(protect_user(victim
, user
, channel
->channel_info
))
1701 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1709 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1711 if(IsService(victim
))
1713 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1717 if(protect_user(victim
, user
, channel
->channel_info
))
1719 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1726 static struct do_not_register
*
1727 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1729 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1730 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1731 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1732 strcpy(dnr
->reason
, reason
);
1734 if(dnr
->chan_name
[0] == '*')
1735 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1736 else if(strpbrk(dnr
->chan_name
, "*?"))
1737 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1739 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1743 static struct dnrList
1744 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1746 struct dnrList list
;
1748 struct do_not_register
*dnr
;
1750 dnrList_init(&list
);
1751 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1752 dnrList_append(&list
, dnr
);
1753 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1754 dnrList_append(&list
, dnr
);
1756 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1757 if(match_ircglob(chan_name
, iter_key(it
)))
1758 dnrList_append(&list
, iter_data(it
));
1763 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1765 struct dnrList list
;
1766 struct do_not_register
*dnr
;
1768 char buf
[INTERVALLEN
];
1770 list
= chanserv_find_dnrs(chan_name
, handle
);
1771 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1773 dnr
= list
.list
[ii
];
1776 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1777 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1780 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1783 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1788 struct do_not_register
*
1789 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1791 struct do_not_register
*dnr
;
1794 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1798 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1800 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1801 if(match_ircglob(chan_name
, iter_key(it
)))
1802 return iter_data(it
);
1807 static CHANSERV_FUNC(cmd_noregister
)
1810 struct do_not_register
*dnr
;
1811 char buf
[INTERVALLEN
];
1812 unsigned int matches
;
1818 reply("CSMSG_DNR_SEARCH_RESULTS");
1819 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1822 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1824 dnr
= iter_data(it
);
1826 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1828 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1831 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1833 dnr
= iter_data(it
);
1835 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1837 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1840 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1842 dnr
= iter_data(it
);
1844 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1846 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1851 reply("MSG_MATCH_COUNT", matches
);
1853 reply("MSG_NO_MATCHES");
1859 if(!IsChannelName(target
) && (*target
!= '*'))
1861 reply("CSMSG_NOT_DNR", target
);
1867 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1868 if((*target
== '*') && !get_handle_info(target
+ 1))
1870 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1873 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1874 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1878 reply("CSMSG_DNR_SEARCH_RESULTS");
1879 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1882 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1884 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1886 reply("MSG_NO_MATCHES");
1890 static CHANSERV_FUNC(cmd_allowregister
)
1892 const char *chan_name
= argv
[1];
1894 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1896 dict_remove(handle_dnrs
, chan_name
+1);
1897 reply("CSMSG_DNR_REMOVED", chan_name
);
1899 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1901 dict_remove(plain_dnrs
, chan_name
);
1902 reply("CSMSG_DNR_REMOVED", chan_name
);
1904 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1906 dict_remove(mask_dnrs
, chan_name
);
1907 reply("CSMSG_DNR_REMOVED", chan_name
);
1911 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1918 chanserv_get_owned_count(struct handle_info
*hi
)
1920 struct userData
*cList
;
1923 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1924 if(cList
->access
== UL_OWNER
)
1929 static CHANSERV_FUNC(cmd_register
)
1931 struct handle_info
*handle
;
1932 struct chanData
*cData
;
1933 struct modeNode
*mn
;
1934 char reason
[MAXLEN
];
1936 unsigned int new_channel
, force
=0;
1937 struct do_not_register
*dnr
;
1943 if(channel
->channel_info
)
1945 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1949 if(channel
->bad_channel
)
1951 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1955 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1957 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1962 chan_name
= channel
->name
;
1968 reply("MSG_MISSING_PARAMS", cmd
->name
);
1969 svccmd_send_help_brief(user
, chanserv
, cmd
);
1972 if(!IsChannelName(argv
[1]))
1974 reply("MSG_NOT_CHANNEL_NAME");
1978 if(opserv_bad_channel(argv
[1]))
1980 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1985 chan_name
= argv
[1];
1988 if(argc
>= (new_channel
+2))
1990 if(!IsHelping(user
))
1992 reply("CSMSG_PROXY_FORBIDDEN");
1996 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1998 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1999 dnr
= chanserv_is_dnr(chan_name
, handle
);
2001 /* Check if they are over the limit.. */
2002 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2004 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2011 handle
= user
->handle_info
;
2012 dnr
= chanserv_is_dnr(chan_name
, handle
);
2013 /* Check if they are over the limit.. */
2014 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2016 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2019 /* Check if another service is in the channel */
2021 for(n
= 0; n
< channel
->members
.used
; n
++)
2023 mn
= channel
->members
.list
[n
];
2024 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2026 reply("CSMSG_ANOTHER_SERVICE");
2033 if(!IsHelping(user
))
2034 reply("CSMSG_DNR_CHANNEL", chan_name
);
2036 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2040 /* now handled above for message specilization *
2041 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2043 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2049 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2051 cData
= register_channel(channel
, user
->handle_info
->handle
);
2052 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2053 cData
->modes
= chanserv_conf
.default_modes
;
2055 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2056 if (IsOffChannel(cData
))
2058 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2062 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2063 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2064 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2066 mod_chanmode_announce(chanserv
, channel
, change
);
2067 mod_chanmode_free(change
);
2070 /* Initialize the channel's max user record. */
2071 cData
->max
= channel
->members
.used
;
2073 if(handle
!= user
->handle_info
)
2074 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2076 reply("CSMSG_REG_SUCCESS", channel
->name
);
2078 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2079 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2084 make_confirmation_string(struct userData
*uData
)
2086 static char strbuf
[16];
2091 for(src
= uData
->handle
->handle
; *src
; )
2092 accum
= accum
* 31 + toupper(*src
++);
2094 for(src
= uData
->channel
->channel
->name
; *src
; )
2095 accum
= accum
* 31 + toupper(*src
++);
2096 sprintf(strbuf
, "%08x", accum
);
2100 static CHANSERV_FUNC(cmd_unregister
)
2103 char reason
[MAXLEN
];
2104 struct chanData
*cData
;
2105 struct userData
*uData
;
2107 cData
= channel
->channel_info
;
2110 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2114 uData
= GetChannelUser(cData
, user
->handle_info
);
2115 if(!uData
|| (uData
->access
< UL_OWNER
))
2117 reply("CSMSG_NO_ACCESS");
2121 if(IsProtected(cData
))
2123 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2127 if(!IsHelping(user
))
2129 const char *confirm_string
;
2130 if(IsSuspended(cData
))
2132 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2135 confirm_string
= make_confirmation_string(uData
);
2136 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2138 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2143 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2144 name
= strdup(channel
->name
);
2145 unregister_channel(cData
, reason
);
2146 reply("CSMSG_UNREG_SUCCESS", name
);
2151 static CHANSERV_FUNC(cmd_move
)
2153 struct mod_chanmode change
;
2154 struct chanNode
*target
;
2155 struct modeNode
*mn
;
2156 struct userData
*uData
;
2157 char reason
[MAXLEN
];
2158 struct do_not_register
*dnr
;
2162 if(IsProtected(channel
->channel_info
))
2164 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2168 if(!IsChannelName(argv
[1]))
2170 reply("MSG_NOT_CHANNEL_NAME");
2174 if(opserv_bad_channel(argv
[1]))
2176 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2180 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2182 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2184 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2186 if(!IsHelping(user
))
2187 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2189 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2195 mod_chanmode_init(&change
);
2196 if(!(target
= GetChannel(argv
[1])))
2198 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2199 if(!IsSuspended(channel
->channel_info
))
2200 AddChannelUser(chanserv
, target
);
2202 else if(target
->channel_info
)
2204 reply("CSMSG_ALREADY_REGGED", target
->name
);
2207 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2208 && !IsHelping(user
))
2210 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2213 else if(!IsSuspended(channel
->channel_info
))
2216 change
.args
[0].mode
= MODE_CHANOP
;
2217 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2218 mod_chanmode_announce(chanserv
, target
, &change
);
2223 /* Clear MODE_REGISTERED from old channel, add it to new. */
2225 change
.modes_clear
= MODE_REGISTERED
;
2226 mod_chanmode_announce(chanserv
, channel
, &change
);
2227 change
.modes_clear
= 0;
2228 change
.modes_set
= MODE_REGISTERED
;
2229 mod_chanmode_announce(chanserv
, target
, &change
);
2232 /* Move the channel_info to the target channel; it
2233 shouldn't be necessary to clear timeq callbacks
2234 for the old channel. */
2235 target
->channel_info
= channel
->channel_info
;
2236 target
->channel_info
->channel
= target
;
2237 channel
->channel_info
= NULL
;
2239 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2241 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2242 if(!IsSuspended(target
->channel_info
))
2244 char reason2
[MAXLEN
];
2245 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2246 DelChannelUser(chanserv
, channel
, reason2
, 0);
2248 UnlockChannel(channel
);
2249 LockChannel(target
);
2250 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2255 merge_users(struct chanData
*source
, struct chanData
*target
)
2257 struct userData
*suData
, *tuData
, *next
;
2263 /* Insert the source's users into the scratch area. */
2264 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2265 dict_insert(merge
, suData
->handle
->handle
, suData
);
2267 /* Iterate through the target's users, looking for
2268 users common to both channels. The lower access is
2269 removed from either the scratch area or target user
2271 for(tuData
= target
->users
; tuData
; tuData
= next
)
2273 struct userData
*choice
;
2275 next
= tuData
->next
;
2277 /* If a source user exists with the same handle as a target
2278 channel's user, resolve the conflict by removing one. */
2279 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2283 /* Pick the data we want to keep. */
2284 /* If the access is the same, use the later seen time. */
2285 if(suData
->access
== tuData
->access
)
2286 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2287 else /* Otherwise, keep the higher access level. */
2288 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2290 /* Remove the user that wasn't picked. */
2291 if(choice
== tuData
)
2293 dict_remove(merge
, suData
->handle
->handle
);
2294 del_channel_user(suData
, 0);
2297 del_channel_user(tuData
, 0);
2300 /* Move the remaining users to the target channel. */
2301 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2303 suData
= iter_data(it
);
2305 /* Insert the user into the target channel's linked list. */
2306 suData
->prev
= NULL
;
2307 suData
->next
= target
->users
;
2308 suData
->channel
= target
;
2311 target
->users
->prev
= suData
;
2312 target
->users
= suData
;
2314 /* Update the user counts for the target channel; the
2315 source counts are left alone. */
2316 target
->userCount
++;
2319 /* Possible to assert (source->users == NULL) here. */
2320 source
->users
= NULL
;
2325 merge_bans(struct chanData
*source
, struct chanData
*target
)
2327 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2329 /* Hold on to the original head of the target ban list
2330 to avoid comparing source bans with source bans. */
2331 tFront
= target
->bans
;
2333 /* Perform a totally expensive O(n*m) merge, ick. */
2334 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2336 /* Flag to track whether the ban's been moved
2337 to the destination yet. */
2340 /* Possible to assert (sbData->prev == NULL) here. */
2341 sNext
= sbData
->next
;
2343 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2345 tNext
= tbData
->next
;
2347 /* Perform two comparisons between each source
2348 and target ban, conflicts are resolved by
2349 keeping the broader ban and copying the later
2350 expiration and triggered time. */
2351 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2353 /* There is a broader ban in the target channel that
2354 overrides one in the source channel; remove the
2355 source ban and break. */
2356 if(sbData
->expires
> tbData
->expires
)
2357 tbData
->expires
= sbData
->expires
;
2358 if(sbData
->triggered
> tbData
->triggered
)
2359 tbData
->triggered
= sbData
->triggered
;
2360 del_channel_ban(sbData
);
2363 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2365 /* There is a broader ban in the source channel that
2366 overrides one in the target channel; remove the
2367 target ban, fall through and move the source over. */
2368 if(tbData
->expires
> sbData
->expires
)
2369 sbData
->expires
= tbData
->expires
;
2370 if(tbData
->triggered
> sbData
->triggered
)
2371 sbData
->triggered
= tbData
->triggered
;
2372 if(tbData
== tFront
)
2374 del_channel_ban(tbData
);
2377 /* Source bans can override multiple target bans, so
2378 we allow a source to run through this loop multiple
2379 times, but we can only move it once. */
2384 /* Remove the source ban from the source ban list. */
2386 sbData
->next
->prev
= sbData
->prev
;
2388 /* Modify the source ban's associated channel. */
2389 sbData
->channel
= target
;
2391 /* Insert the ban into the target channel's linked list. */
2392 sbData
->prev
= NULL
;
2393 sbData
->next
= target
->bans
;
2396 target
->bans
->prev
= sbData
;
2397 target
->bans
= sbData
;
2399 /* Update the user counts for the target channel. */
2404 /* Possible to assert (source->bans == NULL) here. */
2405 source
->bans
= NULL
;
2409 merge_data(struct chanData
*source
, struct chanData
*target
)
2411 /* Use more recent visited and owner-transfer time; use older
2412 * registered time. Bitwise or may_opchan. Use higher max.
2413 * Do not touch last_refresh, ban count or user counts.
2415 if(source
->visited
> target
->visited
)
2416 target
->visited
= source
->visited
;
2417 if(source
->registered
< target
->registered
)
2418 target
->registered
= source
->registered
;
2419 if(source
->ownerTransfer
> target
->ownerTransfer
)
2420 target
->ownerTransfer
= source
->ownerTransfer
;
2421 if(source
->may_opchan
)
2422 target
->may_opchan
= 1;
2423 if(source
->max
> target
->max
)
2424 target
->max
= source
->max
;
2428 merge_channel(struct chanData
*source
, struct chanData
*target
)
2430 merge_users(source
, target
);
2431 merge_bans(source
, target
);
2432 merge_data(source
, target
);
2435 static CHANSERV_FUNC(cmd_merge
)
2437 struct userData
*target_user
;
2438 struct chanNode
*target
;
2439 char reason
[MAXLEN
];
2443 /* Make sure the target channel exists and is registered to the user
2444 performing the command. */
2445 if(!(target
= GetChannel(argv
[1])))
2447 reply("MSG_INVALID_CHANNEL");
2451 if(!target
->channel_info
)
2453 reply("CSMSG_NOT_REGISTERED", target
->name
);
2457 if(IsProtected(channel
->channel_info
))
2459 reply("CSMSG_MERGE_NODELETE");
2463 if(IsSuspended(target
->channel_info
))
2465 reply("CSMSG_MERGE_SUSPENDED");
2469 if(channel
== target
)
2471 reply("CSMSG_MERGE_SELF");
2475 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2476 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2478 reply("CSMSG_MERGE_NOT_OWNER");
2482 /* Merge the channel structures and associated data. */
2483 merge_channel(channel
->channel_info
, target
->channel_info
);
2484 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2485 unregister_channel(channel
->channel_info
, reason
);
2486 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2490 static CHANSERV_FUNC(cmd_opchan
)
2492 struct mod_chanmode change
;
2493 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2495 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2498 channel
->channel_info
->may_opchan
= 0;
2499 mod_chanmode_init(&change
);
2501 change
.args
[0].mode
= MODE_CHANOP
;
2502 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2503 mod_chanmode_announce(chanserv
, channel
, &change
);
2504 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2508 static CHANSERV_FUNC(cmd_adduser
)
2510 struct userData
*actee
;
2511 struct userData
*actor
;
2512 struct handle_info
*handle
;
2513 unsigned short access
;
2517 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2519 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2523 access
= user_level_from_name(argv
[2], UL_OWNER
);
2526 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2530 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2531 if(actor
->access
<= access
)
2533 reply("CSMSG_NO_BUMP_ACCESS");
2537 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2539 // 'kevin must first authenticate with AuthServ.' is sent to user
2540 struct userNode
*unode
;
2541 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2544 if(find_adduser_pending(channel
, unode
)) {
2545 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2548 if(IsInChannel(channel
, unode
)) {
2549 reply("CSMSG_ADDUSER_PENDING");
2550 add_adduser_pending(channel
, unode
, access
);
2551 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2553 /* this results in user must auth AND not in chan errors. too confusing..
2555 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2563 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2565 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2569 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2570 scan_user_presence(actee
, NULL
);
2571 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2575 static CHANSERV_FUNC(cmd_clvl
)
2577 struct handle_info
*handle
;
2578 struct userData
*victim
;
2579 struct userData
*actor
;
2580 unsigned short new_access
;
2581 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2585 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2587 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2590 if(handle
== user
->handle_info
&& !privileged
)
2592 reply("CSMSG_NO_SELF_CLVL");
2596 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2598 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2602 if(actor
->access
<= victim
->access
&& !privileged
)
2604 reply("MSG_USER_OUTRANKED", handle
->handle
);
2608 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2612 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2616 if(new_access
>= actor
->access
&& !privileged
)
2618 reply("CSMSG_NO_BUMP_ACCESS");
2622 victim
->access
= new_access
;
2623 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2627 static CHANSERV_FUNC(cmd_deluser
)
2629 struct handle_info
*handle
;
2630 struct userData
*victim
;
2631 struct userData
*actor
;
2632 unsigned short access
;
2637 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2639 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2642 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2644 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2650 access
= user_level_from_name(argv
[1], UL_OWNER
);
2653 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2656 if(access
!= victim
->access
)
2658 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2664 access
= victim
->access
;
2667 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2669 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2673 chan_name
= strdup(channel
->name
);
2674 del_channel_user(victim
, 1);
2675 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2681 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2683 struct userData
*actor
, *uData
, *next
;
2685 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2687 if(min_access
> max_access
)
2689 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2693 if((actor
->access
<= max_access
) && !IsHelping(user
))
2695 reply("CSMSG_NO_ACCESS");
2699 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2703 if((uData
->access
>= min_access
)
2704 && (uData
->access
<= max_access
)
2705 && match_ircglob(uData
->handle
->handle
, mask
))
2706 del_channel_user(uData
, 1);
2709 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2713 static CHANSERV_FUNC(cmd_mdelowner
)
2715 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2718 static CHANSERV_FUNC(cmd_mdelcoowner
)
2720 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2723 static CHANSERV_FUNC(cmd_mdelmanager
)
2725 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2728 static CHANSERV_FUNC(cmd_mdelop
)
2730 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2733 static CHANSERV_FUNC(cmd_mdelpeon
)
2735 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2738 static CHANSERV_FUNC(cmd_mdelhalfop
)
2740 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2746 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2748 struct banData
*bData
, *next
;
2749 char interval
[INTERVALLEN
];
2754 limit
= now
- duration
;
2755 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2759 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2762 del_channel_ban(bData
);
2766 intervalString(interval
, duration
, user
->handle_info
);
2767 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2772 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2774 struct userData
*actor
, *uData
, *next
;
2775 char interval
[INTERVALLEN
];
2779 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2780 if(min_access
> max_access
)
2782 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2786 if((actor
->access
<= max_access
) && !IsHelping(user
))
2788 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2793 limit
= now
- duration
;
2794 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2798 if((uData
->seen
> limit
)
2800 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2803 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2804 || (!max_access
&& (uData
->access
< actor
->access
)))
2806 del_channel_user(uData
, 1);
2814 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2816 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2820 static CHANSERV_FUNC(cmd_trim
)
2822 unsigned long duration
;
2823 unsigned short min_level
, max_level
;
2828 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2829 duration
= ParseInterval(argv
[2]);
2832 reply("CSMSG_CANNOT_TRIM");
2836 if(!irccasecmp(argv
[1], "lamers"))
2838 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2841 else if(!irccasecmp(argv
[1], "users"))
2843 cmd_trim_users(user
, channel
, 0, 0, duration
, vacation
);
2846 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2848 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
, vacation
);
2851 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2853 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
, vacation
);
2858 reply("CSMSG_INVALID_TRIM", argv
[1]);
2863 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2864 to the user. cmd_all takes advantage of this. */
2865 static CHANSERV_FUNC(cmd_up
)
2867 struct mod_chanmode change
;
2868 struct userData
*uData
;
2871 mod_chanmode_init(&change
);
2873 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2874 if(!change
.args
[0].u
.member
)
2877 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2881 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2885 reply("CSMSG_GODMODE_UP", argv
[0]);
2888 else if(uData
->access
>= UL_OP
)
2890 change
.args
[0].mode
= MODE_CHANOP
;
2891 errmsg
= "CSMSG_ALREADY_OPPED";
2893 else if(uData
->access
>= UL_HALFOP
)
2895 change
.args
[0].mode
= MODE_HALFOP
;
2896 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2898 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2900 change
.args
[0].mode
= MODE_VOICE
;
2901 errmsg
= "CSMSG_ALREADY_VOICED";
2906 reply("CSMSG_NO_ACCESS");
2909 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2910 if(!change
.args
[0].mode
)
2913 reply(errmsg
, channel
->name
);
2916 modcmd_chanmode_announce(&change
);
2920 static CHANSERV_FUNC(cmd_down
)
2922 struct mod_chanmode change
;
2924 mod_chanmode_init(&change
);
2926 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2927 if(!change
.args
[0].u
.member
)
2930 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2934 if(!change
.args
[0].u
.member
->modes
)
2937 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2941 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2942 modcmd_chanmode_announce(&change
);
2946 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
)
2948 struct userData
*cList
;
2950 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2952 if(IsSuspended(cList
->channel
)
2953 || IsUserSuspended(cList
)
2954 || !GetUserMode(cList
->channel
->channel
, user
))
2957 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2963 static CHANSERV_FUNC(cmd_upall
)
2965 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2968 static CHANSERV_FUNC(cmd_downall
)
2970 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2973 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2974 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2977 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
)
2979 unsigned int ii
, valid
;
2980 struct userNode
*victim
;
2981 struct mod_chanmode
*change
;
2983 change
= mod_chanmode_alloc(argc
- 1);
2985 for(ii
=valid
=0; ++ii
< argc
; )
2987 if(!(victim
= GetUserH(argv
[ii
])))
2989 change
->args
[valid
].mode
= mode
;
2990 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2991 if(!change
->args
[valid
].u
.member
)
2993 if(validate
&& !validate(user
, channel
, victim
))
2998 change
->argc
= valid
;
2999 if(valid
< (argc
-1))
3000 reply("CSMSG_PROCESS_FAILED");
3003 modcmd_chanmode_announce(change
);
3004 reply(action
, channel
->name
);
3006 mod_chanmode_free(change
);
3010 static CHANSERV_FUNC(cmd_op
)
3012 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3015 static CHANSERV_FUNC(cmd_hop
)
3017 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3020 static CHANSERV_FUNC(cmd_deop
)
3022 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3025 static CHANSERV_FUNC(cmd_dehop
)
3027 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3030 static CHANSERV_FUNC(cmd_voice
)
3032 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3035 static CHANSERV_FUNC(cmd_devoice
)
3037 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3041 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3047 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3049 struct modeNode
*mn
= channel
->members
.list
[ii
];
3051 if(IsService(mn
->user
))
3054 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3057 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3061 victims
[(*victimCount
)++] = mn
;
3067 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3069 struct userNode
*victim
;
3070 struct modeNode
**victims
;
3071 unsigned int offset
, n
, victimCount
, duration
= 0;
3072 char *reason
= "Bye.", *ban
, *name
;
3073 char interval
[INTERVALLEN
];
3075 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3076 REQUIRE_PARAMS(offset
);
3079 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3080 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3082 /* Truncate the reason to a length of TOPICLEN, as
3083 the ircd does; however, leave room for an ellipsis
3084 and the kicker's nick. */
3085 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3089 if((victim
= GetUserH(argv
[1])))
3091 victims
= alloca(sizeof(victims
[0]));
3092 victims
[0] = GetUserMode(channel
, victim
);
3093 /* XXX: The comparison with ACTION_KICK is just because all
3094 * other actions can work on users outside the channel, and we
3095 * want to allow those (e.g. unbans) in that case. If we add
3096 * some other ejection action for in-channel users, change
3098 victimCount
= victims
[0] ? 1 : 0;
3100 if(IsService(victim
))
3103 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3107 if((action
== ACTION_KICK
) && !victimCount
)
3110 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3114 if(protect_user(victim
, user
, channel
->channel_info
))
3116 // This translates to send_message(user, cmd->parent->bot, ...)
3117 // if user is x3 (ctcp action) cmd is null and segfault.
3119 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3123 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3124 name
= victim
->nick
;
3128 if(!is_ircmask(argv
[1]))
3131 reply("MSG_NICK_UNKNOWN", argv
[1]);
3135 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3137 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3140 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3143 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3144 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3146 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3147 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3148 some creativity, but its not x3's job to be the ban censor anyway. */
3149 if(is_overmask(argv
[1]))
3152 reply("CSMSG_LAME_MASK", argv
[1]);
3156 if((action
== ACTION_KICK
) && (victimCount
== 0))
3159 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3163 name
= ban
= strdup(argv
[1]);
3166 /* Truncate the ban in place if necessary; we must ensure
3167 that 'ban' is a valid ban mask before sanitizing it. */
3168 sanitize_ircmask(ban
);
3170 if(action
& ACTION_ADD_LAMER
)
3172 struct banData
*bData
, *next
;
3174 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3177 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3182 if(action
& ACTION_ADD_TIMED_LAMER
)
3184 duration
= ParseInterval(argv
[2]);
3189 reply("CSMSG_DURATION_TOO_LOW");
3193 else if(duration
> (86400 * 365 * 2))
3196 reply("CSMSG_DURATION_TOO_HIGH");
3203 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3205 if(match_ircglobs(bData
->mask
, ban
))
3207 int exact
= !irccasecmp(bData
->mask
, ban
);
3209 /* The ban is redundant; there is already a ban
3210 with the same effect in place. */
3214 free(bData
->reason
);
3215 bData
->reason
= strdup(reason
);
3216 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3218 reply("CSMSG_REASON_CHANGE", ban
);
3222 if(exact
&& bData
->expires
)
3226 /* If the ban matches an existing one exactly,
3227 extend the expiration time if the provided
3228 duration is longer. */
3229 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3231 bData
->expires
= now
+ duration
;
3242 /* Delete the expiration timeq entry and
3243 requeue if necessary. */
3244 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3247 timeq_add(bData
->expires
, expire_ban
, bData
);
3251 /* automated kickban, dont reply */
3254 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3256 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3262 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3269 if(match_ircglobs(ban
, bData
->mask
))
3271 /* The ban we are adding makes previously existing
3272 bans redundant; silently remove them. */
3273 del_channel_ban(bData
);
3277 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
);
3279 name
= ban
= strdup(bData
->mask
);
3283 /* WHAT DOES THIS DO?? -Rubin */
3284 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3286 extern const char *hidden_host_suffix
;
3287 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3289 unsigned int l1
, l2
;
3292 l2
= strlen(old_name
);
3295 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3297 new_mask
= malloc(MAXLEN
);
3298 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3300 name
= ban
= new_mask
;
3305 if(action
& ACTION_BAN
)
3307 unsigned int exists
;
3308 struct mod_chanmode
*change
;
3310 if(channel
->banlist
.used
>= MAXBANS
)
3313 reply("CSMSG_BANLIST_FULL", channel
->name
);
3318 exists
= ChannelBanExists(channel
, ban
);
3319 change
= mod_chanmode_alloc(victimCount
+ 1);
3320 for(n
= 0; n
< victimCount
; ++n
)
3322 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3323 change
->args
[n
].u
.member
= victims
[n
];
3327 change
->args
[n
].mode
= MODE_BAN
;
3328 change
->args
[n
++].u
.hostmask
= ban
;
3332 modcmd_chanmode_announce(change
);
3334 mod_chanmode_announce(chanserv
, channel
, change
);
3335 mod_chanmode_free(change
);
3337 if(exists
&& (action
== ACTION_BAN
))
3340 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3346 if(action
& ACTION_KICK
)
3348 char kick_reason
[MAXLEN
];
3349 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3351 for(n
= 0; n
< victimCount
; n
++)
3352 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3357 /* No response, since it was automated. */
3359 else if(action
& ACTION_ADD_LAMER
)
3362 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3364 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3366 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3367 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3368 else if(action
& ACTION_BAN
)
3369 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3370 else if(action
& ACTION_KICK
&& victimCount
)
3371 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3377 static CHANSERV_FUNC(cmd_kickban
)
3379 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3382 static CHANSERV_FUNC(cmd_kick
)
3384 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3387 static CHANSERV_FUNC(cmd_ban
)
3389 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3392 static CHANSERV_FUNC(cmd_addlamer
)
3394 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3397 static CHANSERV_FUNC(cmd_addtimedlamer
)
3399 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3402 static struct mod_chanmode
*
3403 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3405 struct mod_chanmode
*change
;
3406 unsigned char *match
;
3407 unsigned int ii
, count
;
3409 match
= alloca(bans
->used
);
3412 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3414 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3415 MATCH_USENICK
| MATCH_VISIBLE
);
3422 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3424 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3431 change
= mod_chanmode_alloc(count
);
3432 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3436 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3437 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3439 assert(count
== change
->argc
);
3443 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3445 unsigned int jj
, ii
, count
;
3447 struct chanData
*channel
;
3449 struct mod_chanmode
*change
;
3451 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3452 /* Walk through every channel */
3453 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3454 switch(channel
->chOpts
[chBanTimeout
])
3456 default: case '0': continue; /* Dont remove bans in this chan */
3457 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3458 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3459 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3460 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3461 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3464 /* First find out how many bans were going to unset */
3465 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3466 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3470 /* At least one ban, so setup a removal */
3471 change
= mod_chanmode_alloc(count
);
3473 /* Walk over every ban in this channel.. */
3474 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3475 bn
= channel
->channel
->banlist
.list
[jj
];
3476 if (bn
->set
< bantimeout
) {
3477 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3479 /* Add this ban to the mode change */
3480 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3481 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3483 /* Pull this ban out of the list */
3484 banList_remove(&(channel
->channel
->banlist
), bn
);
3489 /* Send the modes to IRC */
3490 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3492 /* free memory from strdup above */
3493 for(ii
= 0; ii
< count
; ++ii
)
3494 free((char*)change
->args
[ii
].u
.hostmask
);
3496 mod_chanmode_free(change
);
3499 /* Set this function to run again */
3500 if(chanserv_conf
.ban_timeout_frequency
)
3501 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3506 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3508 struct userNode
*actee
;
3514 /* may want to allow a comma delimited list of users... */
3515 if(!(actee
= GetUserH(argv
[1])))
3517 if(!is_ircmask(argv
[1]))
3519 reply("MSG_NICK_UNKNOWN", argv
[1]);
3523 mask
= strdup(argv
[1]);
3526 /* We don't sanitize the mask here because ircu
3528 if(action
& ACTION_UNBAN
)
3530 struct mod_chanmode
*change
;
3531 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3536 modcmd_chanmode_announce(change
);
3537 for(ii
= 0; ii
< change
->argc
; ++ii
)
3538 free((char*)change
->args
[ii
].u
.hostmask
);
3539 mod_chanmode_free(change
);
3544 if(action
& ACTION_DEL_LAMER
)
3546 struct banData
*ban
, *next
;
3548 ban
= channel
->channel_info
->bans
; /* lamers */
3552 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3555 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3560 del_channel_ban(ban
);
3567 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3569 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3575 static CHANSERV_FUNC(cmd_unban
)
3577 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3580 static CHANSERV_FUNC(cmd_dellamer
)
3582 /* it doesn't necessarily have to remove the channel ban - may want
3583 to make that an option. */
3584 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3587 static CHANSERV_FUNC(cmd_unbanme
)
3589 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3590 long flags
= ACTION_UNBAN
;
3592 /* remove permanent bans if the user has the proper access. */
3593 if(uData
->access
>= UL_MANAGER
)
3594 flags
|= ACTION_DEL_LAMER
;
3596 argv
[1] = user
->nick
;
3597 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3600 static CHANSERV_FUNC(cmd_unbanall
)
3602 struct mod_chanmode
*change
;
3605 if(!channel
->banlist
.used
)
3607 reply("CSMSG_NO_BANS", channel
->name
);
3611 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3612 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3614 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3615 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3617 modcmd_chanmode_announce(change
);
3618 for(ii
= 0; ii
< change
->argc
; ++ii
)
3619 free((char*)change
->args
[ii
].u
.hostmask
);
3620 mod_chanmode_free(change
);
3621 reply("CSMSG_BANS_REMOVED", channel
->name
);
3625 static CHANSERV_FUNC(cmd_open
)
3627 struct mod_chanmode
*change
;
3630 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3632 change
= mod_chanmode_alloc(0);
3633 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3634 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3635 && channel
->channel_info
->modes
.modes_set
)
3636 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3637 modcmd_chanmode_announce(change
);
3638 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3639 for(ii
= 0; ii
< change
->argc
; ++ii
)
3640 free((char*)change
->args
[ii
].u
.hostmask
);
3641 mod_chanmode_free(change
);
3645 static CHANSERV_FUNC(cmd_myaccess
)
3647 static struct string_buffer sbuf
;
3648 struct handle_info
*target_handle
;
3649 struct userData
*uData
;
3652 target_handle
= user
->handle_info
;
3653 else if(!IsHelping(user
))
3655 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3658 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3661 if(!target_handle
->channels
)
3663 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3667 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3668 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3670 struct chanData
*cData
= uData
->channel
;
3672 if(uData
->access
> UL_OWNER
)
3674 if(IsProtected(cData
)
3675 && (target_handle
!= user
->handle_info
)
3676 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3679 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3680 if(uData
->flags
== USER_AUTO_OP
)
3681 string_buffer_append(&sbuf
, ',');
3682 if(IsUserSuspended(uData
))
3683 string_buffer_append(&sbuf
, 's');
3684 if(IsUserAutoOp(uData
))
3686 if(uData
->access
>= UL_OP
)
3687 string_buffer_append(&sbuf
, 'o');
3688 else if(uData
->access
>= UL_HALFOP
)
3689 string_buffer_append(&sbuf
, 'h');
3690 else if(uData
->access
>= UL_PEON
)
3691 string_buffer_append(&sbuf
, 'v');
3693 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3694 string_buffer_append(&sbuf
, 'i');
3696 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3698 string_buffer_append_string(&sbuf
, ")]");
3699 string_buffer_append(&sbuf
, '\0');
3700 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3706 static CHANSERV_FUNC(cmd_access
)
3708 struct userNode
*target
;
3709 struct handle_info
*target_handle
;
3710 struct userData
*uData
;
3712 char prefix
[MAXLEN
];
3717 target_handle
= target
->handle_info
;
3719 else if((target
= GetUserH(argv
[1])))
3721 target_handle
= target
->handle_info
;
3723 else if(argv
[1][0] == '*')
3725 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3727 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3733 reply("MSG_NICK_UNKNOWN", argv
[1]);
3737 assert(target
|| target_handle
);
3739 if(target
== chanserv
)
3741 reply("CSMSG_IS_CHANSERV");
3749 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3754 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3757 reply("MSG_AUTHENTICATE");
3763 const char *epithet
= NULL
, *type
= NULL
;
3766 epithet
= chanserv_conf
.irc_operator_epithet
;
3769 else if(IsNetworkHelper(target
))
3771 epithet
= chanserv_conf
.network_helper_epithet
;
3772 type
= "network helper";
3774 else if(IsSupportHelper(target
))
3776 epithet
= chanserv_conf
.support_helper_epithet
;
3777 type
= "support helper";
3781 if(target_handle
->epithet
)
3782 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3784 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3786 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3790 sprintf(prefix
, "%s", target_handle
->handle
);
3793 if(!channel
->channel_info
)
3795 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3799 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3800 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3801 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3803 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3804 /* To prevent possible information leaks, only show infolines
3805 * if the requestor is in the channel or it's their own
3807 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3809 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3811 /* Likewise, only say it's suspended if the user has active
3812 * access in that channel or it's their own entry. */
3813 if(IsUserSuspended(uData
)
3814 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3815 || (user
->handle_info
== uData
->handle
)))
3817 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3822 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3828 /* This is never used...
3830 zoot_list(struct listData *list)
3832 struct userData *uData;
3833 unsigned int start, curr, highest, lowest;
3834 struct helpfile_table tmp_table;
3835 const char **temp, *msg;
3837 if(list->table.length == 1)
3840 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);
3842 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));
3843 msg = user_find_message(list->user, "MSG_NONE");
3844 send_message_type(4, list->user, list->bot, " %s", msg);
3846 tmp_table.width = list->table.width;
3847 tmp_table.flags = list->table.flags;
3848 list->table.contents[0][0] = " ";
3849 highest = list->highest;
3850 if(list->lowest != 0)
3851 lowest = list->lowest;
3852 else if(highest < 100)
3855 lowest = highest - 100;
3856 for(start = curr = 1; curr < list->table.length; )
3858 uData = list->users[curr-1];
3859 list->table.contents[curr++][0] = " ";
3860 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3863 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);
3865 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));
3866 temp = list->table.contents[--start];
3867 list->table.contents[start] = list->table.contents[0];
3868 tmp_table.contents = list->table.contents + start;
3869 tmp_table.length = curr - start;
3870 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3871 list->table.contents[start] = temp;
3873 highest = lowest - 1;
3874 lowest = (highest < 100) ? 0 : (highest - 99);
3881 normal_list(struct listData
*list
)
3885 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
), list
->search
);
3887 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list
->channel
->name
, user_level_name_from_level(list
->lowest
), user_level_name_from_level(list
->highest
));
3888 if(list
->table
.length
== 1)
3890 msg
= user_find_message(list
->user
, "MSG_NONE");
3891 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3894 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3897 /* if these need changed, uncomment and customize
3899 clean_list(struct listData *list)
3903 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3905 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3906 if(list->table.length == 1)
3908 msg = user_find_message(list->user, "MSG_NONE");
3909 send_message_type(4, list->user, list->bot, " %s", msg);
3912 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3916 advanced_list(struct listData *list)
3920 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3922 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3923 if(list->table.length == 1)
3925 msg = user_find_message(list->user, "MSG_NONE");
3926 send_message_type(4, list->user, list->bot, " %s", msg);
3929 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3933 classic_list(struct listData *list)
3937 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
3939 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
3940 if(list->table.length == 1)
3942 msg = user_find_message(list->user, "MSG_NONE");
3943 send_message_type(4, list->user, list->bot, " %s", msg);
3946 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3951 userData_access_comp(const void *arg_a
, const void *arg_b
)
3953 const struct userData
*a
= *(struct userData
**)arg_a
;
3954 const struct userData
*b
= *(struct userData
**)arg_b
;
3956 if(a
->access
!= b
->access
)
3957 res
= b
->access
- a
->access
;
3959 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3964 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3966 void (*send_list
)(struct listData
*);
3967 struct userData
*uData
;
3968 struct listData lData
;
3969 unsigned int matches
;
3975 lData
.bot
= cmd
->parent
->bot
;
3976 lData
.channel
= channel
;
3977 lData
.lowest
= lowest
;
3978 lData
.highest
= highest
;
3979 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3980 send_list
= normal_list
;
3981 /* What does the following line do exactly?? */
3982 /*(void)zoot_list; ** since it doesn't show user levels */
3985 if(user->handle_info)
3987 switch(user->handle_info->userlist_style)
3989 case HI_STYLE_CLEAN:
3990 send_list = clean_list;
3992 case HI_STYLE_ADVANCED:
3993 send_list = advanced_list;
3995 case HI_STYLE_CLASSIC:
3996 send_list = classic_list;
3998 case HI_STYLE_NORMAL:
4000 send_list = normal_list;
4005 send_list
= normal_list
;
4007 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
4009 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4011 if((uData
->access
< lowest
)
4012 || (uData
->access
> highest
)
4013 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
4015 lData
.users
[matches
++] = uData
;
4017 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
4019 lData
.table
.length
= matches
+1;
4020 lData
.table
.flags
= TABLE_NO_FREE
;
4021 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4023 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4024 lData
.table
.width
= 5; /* with level = 5 */
4026 lData
.table
.width
= 4; /* without = 4 */
4027 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4028 lData
.table
.contents
[0] = ary
;
4029 if(user
->handle_info
) {
4030 switch(user
->handle_info
->userlist_style
) {
4031 case HI_STYLE_CLASSIC
:
4034 case HI_STYLE_ADVANCED
:
4035 ary
[i
++] = "Access";
4038 case HI_STYLE_CLEAN
:
4039 ary
[i
++] = "Access";
4041 case HI_STYLE_NORMAL
:
4043 ary
[i
++] = "Access";
4048 ary
[i
++] = "Access";
4050 ary
[i
++] = "Account";
4051 ary
[i
] = "Last Seen";
4053 ary
[i
++] = "Status";
4054 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4056 struct userData
*uData
= lData
.users
[matches
-1];
4057 char seen
[INTERVALLEN
];
4060 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4061 lData
.table
.contents
[matches
] = ary
;
4062 if(user
->handle_info
) {
4063 switch(user
->handle_info
->userlist_style
) {
4064 case HI_STYLE_CLASSIC
:
4065 ary
[i
++] = strtab(uData
->access
);
4067 case HI_STYLE_ADVANCED
:
4068 ary
[i
++] = user_level_name_from_level(uData
->access
);
4069 ary
[i
++] = strtab(uData
->access
);
4071 case HI_STYLE_CLEAN
:
4072 ary
[i
++] = user_level_name_from_level(uData
->access
);
4074 case HI_STYLE_NORMAL
:
4076 ary
[i
++] = user_level_name_from_level(uData
->access
);
4081 ary
[i
++] = user_level_name_from_level(uData
->access
);
4083 ary
[i
++] = uData
->handle
->handle
;
4086 else if(!uData
->seen
)
4089 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4090 ary
[i
] = strdup(ary
[i
]);
4092 if(IsUserSuspended(uData
))
4093 ary
[i
++] = "Suspended";
4094 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4095 ary
[i
++] = "Vacation";
4097 ary
[i
++] = "Normal";
4100 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4102 /* Free strdup above */
4103 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4104 free(lData
.table
.contents
[matches
]);
4106 free(lData
.table
.contents
[0]);
4107 free(lData
.table
.contents
);
4111 /* Remove this now that debugging is over? or improve it for
4112 * users? Would it be better tied into USERS somehow? -Rubin */
4113 static CHANSERV_FUNC(cmd_pending
)
4115 struct adduserPending
*ap
;
4116 reply("CSMSG_ADDUSER_PENDING_HEADER");
4117 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4119 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4120 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4121 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4125 static CHANSERV_FUNC(cmd_users
)
4127 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4130 static CHANSERV_FUNC(cmd_wlist
)
4132 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4135 static CHANSERV_FUNC(cmd_clist
)
4137 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4140 static CHANSERV_FUNC(cmd_mlist
)
4142 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4145 static CHANSERV_FUNC(cmd_olist
)
4147 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4150 static CHANSERV_FUNC(cmd_hlist
)
4152 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4155 static CHANSERV_FUNC(cmd_plist
)
4157 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4160 static CHANSERV_FUNC(cmd_lamers
)
4162 struct helpfile_table tbl
;
4163 unsigned int matches
= 0, timed
= 0, ii
;
4164 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4165 const char *msg_never
, *triggered
, *expires
;
4166 struct banData
*ban
, **bans
; /* lamers */
4173 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4174 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4177 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4179 if(search
&& !match_ircglobs(search
, ban
->mask
))
4181 bans
[matches
++] = ban
;
4186 tbl
.length
= matches
+ 1;
4187 tbl
.width
= 4 + timed
;
4189 tbl
.flags
= TABLE_NO_FREE
;
4190 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4191 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4192 tbl
.contents
[0][0] = "Mask";
4193 tbl
.contents
[0][1] = "Set By";
4194 tbl
.contents
[0][2] = "Triggered";
4197 tbl
.contents
[0][3] = "Expires";
4198 tbl
.contents
[0][4] = "Reason";
4201 tbl
.contents
[0][3] = "Reason";
4204 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4206 free(tbl
.contents
[0]);
4211 msg_never
= user_find_message(user
, "MSG_NEVER");
4212 for(ii
= 0; ii
< matches
; )
4218 else if(ban
->expires
)
4219 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4221 expires
= msg_never
;
4224 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4226 triggered
= msg_never
;
4228 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4229 tbl
.contents
[ii
][0] = ban
->mask
;
4230 tbl
.contents
[ii
][1] = ban
->owner
;
4231 tbl
.contents
[ii
][2] = strdup(triggered
);
4234 tbl
.contents
[ii
][3] = strdup(expires
);
4235 tbl
.contents
[ii
][4] = ban
->reason
;
4238 tbl
.contents
[ii
][3] = ban
->reason
;
4240 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4241 /* reply("MSG_MATCH_COUNT", matches); */
4242 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4244 free((char*)tbl
.contents
[ii
][2]);
4246 free((char*)tbl
.contents
[ii
][3]);
4247 free(tbl
.contents
[ii
]);
4249 free(tbl
.contents
[0]);
4256 * return + if the user does NOT have the right to set the topic, and
4257 * the topic is changed.
4260 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4262 struct chanData
*cData
= channel
->channel_info
;
4263 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4265 else if(cData
->topic
)
4266 return irccasecmp(new_topic
, cData
->topic
);
4273 * Makes a givin topic fit into a givin topic mask and returns
4276 * topic_mask - the mask to conform to
4277 * topic - the topic to make conform
4278 * new_topic - the pre-allocated char* to put the new topic into
4280 * modifies: new_topic
4283 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4285 //char *topic_mask = cData->topic_mask;
4287 int pos
=0, starpos
=-1, dpos
=0, len
;
4289 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4296 strcpy(new_topic
, "");
4299 len
= strlen(topic
);
4300 if((dpos
+ len
) > TOPICLEN
)
4301 len
= TOPICLEN
+ 1 - dpos
;
4302 memcpy(new_topic
+dpos
, topic
, len
);
4306 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4307 default: new_topic
[dpos
++] = tchar
; break;
4310 if((dpos
> TOPICLEN
) || tchar
)
4312 strcpy(new_topic
, "");
4315 new_topic
[dpos
] = 0;
4319 static CHANSERV_FUNC(cmd_topic
)
4321 struct chanData
*cData
;
4325 #ifdef WITH_PROTOCOL_P10
4329 cData
= channel
->channel_info
;
4334 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4335 reply("CSMSG_TOPIC_SET", cData
->topic
);
4339 reply("CSMSG_NO_TOPIC", channel
->name
);
4343 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4344 /* If they say "!topic *", use an empty topic. */
4345 if((topic
[0] == '*') && (topic
[1] == 0))
4348 if(bad_topic(channel
, user
, topic
))
4350 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4355 /* If there is a topicmask set, and the new topic doesnt match, make it */
4356 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4358 char *topic_mask
= cData
->topic_mask
;
4359 char new_topic
[TOPICLEN
+1];
4361 /* make a new topic fitting mask */
4362 conform_topic(topic_mask
, topic
, new_topic
);
4365 /* Topic couldnt fit into mask, was too long */
4366 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4367 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4370 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4372 else /* No mask set, just set the topic */
4373 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4376 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4378 /* Grab the topic and save it as the default topic. */
4380 cData
->topic
= strdup(channel
->topic
);
4386 static CHANSERV_FUNC(cmd_mode
)
4388 struct userData
*uData
;
4389 struct mod_chanmode
*change
;
4394 change
= &channel
->channel_info
->modes
;
4395 if(change
->modes_set
|| change
->modes_clear
) {
4396 modcmd_chanmode_announce(change
);
4397 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4399 reply("CSMSG_NO_MODES", channel
->name
);
4403 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4405 base_oplevel
= MAXOPLEVEL
;
4406 else if (uData
->access
>= UL_OWNER
)
4409 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4410 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4414 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4418 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4419 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4422 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4423 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4427 modcmd_chanmode_announce(change
);
4428 mod_chanmode_free(change
);
4429 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4433 static CHANSERV_FUNC(cmd_invite
)
4435 struct userData
*uData
;
4436 struct userNode
*invite
;
4438 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4442 if(!(invite
= GetUserH(argv
[1])))
4444 reply("MSG_NICK_UNKNOWN", argv
[1]);
4451 if(GetUserMode(channel
, invite
))
4453 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4461 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4462 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4465 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4467 irc_invite(chanserv
, invite
, channel
);
4469 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4474 static CHANSERV_FUNC(cmd_inviteme
)
4476 if(GetUserMode(channel
, user
))
4478 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4481 if(channel
->channel_info
4482 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4484 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4487 irc_invite(cmd
->parent
->bot
, user
, channel
);
4492 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4495 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4497 /* We display things based on two dimensions:
4498 * - Issue time: present or absent
4499 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4500 * (in order of precedence, so something both expired and revoked
4501 * only counts as revoked)
4503 combo
= (suspended
->issued
? 4 : 0)
4504 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4506 case 0: /* no issue time, indefinite expiration */
4507 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4509 case 1: /* no issue time, expires in future */
4510 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4511 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4513 case 2: /* no issue time, expired */
4514 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4515 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4517 case 3: /* no issue time, revoked */
4518 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4519 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4521 case 4: /* issue time set, indefinite expiration */
4522 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4523 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4525 case 5: /* issue time set, expires in future */
4526 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4527 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4528 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4530 case 6: /* issue time set, expired */
4531 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4532 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4533 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4535 case 7: /* issue time set, revoked */
4536 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4537 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4538 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4541 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4547 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4550 const char *fmt
= "%a %b %d %H:%M %Y";
4551 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4553 if(giveownership
->staff_issuer
)
4555 if(giveownership
->reason
)
4556 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4557 giveownership
->target
, giveownership
->target_access
,
4558 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4560 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4561 giveownership
->target
, giveownership
->target_access
,
4562 giveownership
->staff_issuer
, buf
);
4566 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4571 static CHANSERV_FUNC(cmd_info
)
4573 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4574 struct userData
*uData
, *owner
;
4575 struct chanData
*cData
;
4576 struct do_not_register
*dnr
;
4581 cData
= channel
->channel_info
;
4582 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4583 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4586 uData
= GetChannelUser(cData
, user
->handle_info
);
4587 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4589 mod_chanmode_format(&cData
->modes
, modes
);
4590 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4591 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4594 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4598 note
= iter_data(it
);
4599 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4602 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4603 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4606 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4607 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4608 if(owner
->access
== UL_OWNER
)
4609 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4610 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4611 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4612 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4614 privileged
= IsStaff(user
);
4616 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4617 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4618 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4620 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4621 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4623 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4625 struct suspended
*suspended
;
4626 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4627 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4628 show_suspension_info(cmd
, user
, suspended
);
4630 else if(IsSuspended(cData
))
4632 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4633 show_suspension_info(cmd
, user
, cData
->suspended
);
4635 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4637 struct giveownership
*giveownership
;
4638 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4639 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4640 show_giveownership_info(cmd
, user
, giveownership
);
4642 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4643 reply("CSMSG_CHANNEL_END");
4645 reply("CSMSG_CHANNEL_END_CLEAN");
4649 static CHANSERV_FUNC(cmd_netinfo
)
4651 extern time_t boot_time
;
4652 extern unsigned long burst_length
;
4653 char interval
[INTERVALLEN
];
4655 reply("CSMSG_NETWORK_INFO");
4656 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4657 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4658 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4659 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4660 reply("CSMSG_NETWORK_LAMERS", banCount
);
4661 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4662 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4663 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4668 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4670 struct helpfile_table table
;
4672 struct userNode
*user
;
4677 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4678 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4679 for(nn
=0; nn
<list
->used
; nn
++)
4681 user
= list
->list
[nn
];
4682 if(user
->modes
& skip_flags
)
4686 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4689 nick
= alloca(strlen(user
->nick
)+3);
4690 sprintf(nick
, "(%s)", user
->nick
);
4694 table
.contents
[table
.length
][0] = nick
;
4697 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4700 static CHANSERV_FUNC(cmd_ircops
)
4702 reply("CSMSG_STAFF_OPERS");
4703 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4707 static CHANSERV_FUNC(cmd_helpers
)
4709 reply("CSMSG_STAFF_HELPERS");
4710 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4714 static CHANSERV_FUNC(cmd_staff
)
4716 reply("CSMSG_NETWORK_STAFF");
4717 cmd_ircops(CSFUNC_ARGS
);
4718 cmd_helpers(CSFUNC_ARGS
);
4722 static CHANSERV_FUNC(cmd_peek
)
4724 struct modeNode
*mn
;
4725 char modes
[MODELEN
];
4727 struct helpfile_table table
;
4729 irc_make_chanmode(channel
, modes
);
4731 reply("CSMSG_PEEK_INFO", channel
->name
);
4732 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4734 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4735 reply("CSMSG_PEEK_MODES", modes
);
4736 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4740 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4741 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4742 for(n
= 0; n
< channel
->members
.used
; n
++)
4744 mn
= channel
->members
.list
[n
];
4745 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4747 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4748 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4753 reply("CSMSG_PEEK_OPS");
4754 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4757 reply("CSMSG_PEEK_NO_OPS");
4758 reply("CSMSG_PEEK_END");
4762 static MODCMD_FUNC(cmd_wipeinfo
)
4764 struct handle_info
*victim
;
4765 struct userData
*ud
, *actor
;
4768 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4769 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4771 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4773 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4776 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4778 reply("MSG_USER_OUTRANKED", victim
->handle
);
4784 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4788 static CHANSERV_FUNC(cmd_resync
)
4790 struct mod_chanmode
*changes
;
4791 struct chanData
*cData
= channel
->channel_info
;
4792 unsigned int ii
, used
;
4794 /* 6 = worst case -ovh+ovh on everyone */
4795 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4796 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4798 struct modeNode
*mn
= channel
->members
.list
[ii
];
4799 struct userData
*uData
;
4801 if(IsService(mn
->user
))
4805 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4807 /* If the channel is in no-mode mode, de-mode EVERYONE */
4808 if(cData
->chOpts
[chAutomode
] == 'n')
4812 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4813 changes
->args
[used
++].u
.member
= mn
;
4816 else /* Give various userlevels their modes.. */
4818 if(uData
&& uData
->access
>= UL_OP
)
4820 if(!(mn
->modes
& MODE_CHANOP
))
4822 changes
->args
[used
].mode
= MODE_CHANOP
;
4823 changes
->args
[used
++].u
.member
= mn
;
4826 else if(uData
&& uData
->access
>= UL_HALFOP
)
4828 if(mn
->modes
& MODE_CHANOP
)
4830 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4831 changes
->args
[used
++].u
.member
= mn
;
4833 if(!(mn
->modes
& MODE_HALFOP
))
4835 changes
->args
[used
].mode
= MODE_HALFOP
;
4836 changes
->args
[used
++].u
.member
= mn
;
4839 else if(uData
&& uData
->access
>= UL_PEON
)
4841 if(mn
->modes
& MODE_CHANOP
)
4843 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4844 changes
->args
[used
++].u
.member
= mn
;
4846 if(mn
->modes
& MODE_HALFOP
)
4848 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4849 changes
->args
[used
++].u
.member
= mn
;
4851 /* Don't voice peons if were in mode m */
4852 if( cData
->chOpts
[chAutomode
] == 'm')
4854 if(mn
->modes
& MODE_VOICE
)
4856 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4857 changes
->args
[used
++].u
.member
= mn
;
4860 /* otherwise, make user they do have voice */
4861 else if(!(mn
->modes
& MODE_VOICE
))
4863 changes
->args
[used
].mode
= MODE_VOICE
;
4864 changes
->args
[used
++].u
.member
= mn
;
4867 else /* They arnt on the userlist.. */
4869 /* If we voice everyone, but they dont.. */
4870 if(cData
->chOpts
[chAutomode
] == 'v')
4872 /* Remove anything except v */
4873 if(mn
->modes
& ~MODE_VOICE
)
4875 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4876 changes
->args
[used
++].u
.member
= mn
;
4879 if(!(mn
->modes
& MODE_VOICE
))
4881 changes
->args
[used
].mode
= MODE_VOICE
;
4882 changes
->args
[used
++].u
.member
= mn
;
4885 /* If we hop everyone, but they dont.. */
4886 else if(cData
->chOpts
[chAutomode
] == 'h')
4888 /* Remove anything except h */
4889 if(mn
->modes
& ~MODE_HALFOP
)
4891 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4892 changes
->args
[used
++].u
.member
= mn
;
4895 if(!(mn
->modes
& MODE_HALFOP
))
4897 changes
->args
[used
].mode
= MODE_HALFOP
;
4898 changes
->args
[used
++].u
.member
= mn
;
4901 /* If we op everyone, but they dont.. */
4902 else if(cData
->chOpts
[chAutomode
] == 'o')
4904 /* Remove anything except h */
4905 if(mn
->modes
& ~MODE_CHANOP
)
4907 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4908 changes
->args
[used
++].u
.member
= mn
;
4911 if(!(mn
->modes
& MODE_CHANOP
))
4913 changes
->args
[used
].mode
= MODE_CHANOP
;
4914 changes
->args
[used
++].u
.member
= mn
;
4917 /* they have no excuse for having modes, de-everything them */
4922 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4923 changes
->args
[used
++].u
.member
= mn
;
4929 changes
->argc
= used
;
4930 modcmd_chanmode_announce(changes
);
4931 mod_chanmode_free(changes
);
4932 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4936 static CHANSERV_FUNC(cmd_seen
)
4938 struct userData
*uData
;
4939 struct handle_info
*handle
;
4940 char seen
[INTERVALLEN
];
4944 if(!irccasecmp(argv
[1], chanserv
->nick
))
4946 reply("CSMSG_IS_CHANSERV");
4950 if(!(handle
= get_handle_info(argv
[1])))
4952 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4956 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4958 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4963 reply("CSMSG_USER_PRESENT", handle
->handle
);
4964 else if(uData
->seen
)
4965 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4967 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4969 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4970 reply("CSMSG_USER_VACATION", handle
->handle
);
4975 static MODCMD_FUNC(cmd_names
)
4977 struct userNode
*targ
;
4978 struct userData
*targData
;
4979 unsigned int ii
, pos
;
4982 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4984 targ
= channel
->members
.list
[ii
]->user
;
4985 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4988 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4991 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4995 if(IsUserSuspended(targData
))
4997 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
5000 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
5001 reply("CSMSG_END_NAMES", channel
->name
);
5006 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5008 switch(ntype
->visible_type
)
5010 case NOTE_VIS_ALL
: return 1;
5011 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
5012 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
5017 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
5019 struct userData
*uData
;
5021 switch(ntype
->set_access_type
)
5023 case NOTE_SET_CHANNEL_ACCESS
:
5024 if(!user
->handle_info
)
5026 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
5028 return uData
->access
>= ntype
->set_access
.min_ulevel
;
5029 case NOTE_SET_CHANNEL_SETTER
:
5030 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
5031 case NOTE_SET_PRIVILEGED
: default:
5032 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
5036 static CHANSERV_FUNC(cmd_note
)
5038 struct chanData
*cData
;
5040 struct note_type
*ntype
;
5042 cData
= channel
->channel_info
;
5045 reply("CSMSG_NOT_REGISTERED", channel
->name
);
5049 /* If no arguments, show all visible notes for the channel. */
5055 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5057 note
= iter_data(it
);
5058 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5061 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5062 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5065 reply("CSMSG_NOTELIST_END", channel
->name
);
5067 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5069 /* If one argument, show the named note. */
5072 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5073 && note_type_visible_to_user(cData
, note
->type
, user
))
5075 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5077 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5078 && note_type_visible_to_user(NULL
, ntype
, user
))
5080 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5085 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5089 /* Assume they're trying to set a note. */
5093 ntype
= dict_find(note_types
, argv
[1], NULL
);
5096 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5099 else if(note_type_settable_by_user(channel
, ntype
, user
))
5101 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5102 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5103 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5104 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5105 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5107 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5109 /* The note is viewable to staff only, so return 0
5110 to keep the invocation from getting logged (or
5111 regular users can see it in !events). */
5117 reply("CSMSG_NO_ACCESS");
5124 static CHANSERV_FUNC(cmd_delnote
)
5129 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5130 || !note_type_settable_by_user(channel
, note
->type
, user
))
5132 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5135 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5136 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5140 static CHANSERV_FUNC(cmd_last
)
5146 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5148 if(numoflines
< 1 || numoflines
> 200)
5150 reply("CSMSG_LAST_INVALID");
5153 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5157 static CHANSERV_FUNC(cmd_events
)
5159 struct logSearch discrim
;
5160 struct logReport report
;
5161 unsigned int matches
, limit
;
5163 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5164 if(limit
< 1 || limit
> 200)
5167 memset(&discrim
, 0, sizeof(discrim
));
5168 discrim
.masks
.bot
= chanserv
;
5169 discrim
.masks
.channel_name
= channel
->name
;
5171 discrim
.masks
.command
= argv
[2];
5172 discrim
.limit
= limit
;
5173 discrim
.max_time
= INT_MAX
;
5174 discrim
.severities
= 1 << LOG_COMMAND
;
5175 report
.reporter
= chanserv
;
5177 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5178 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5180 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5182 reply("MSG_MATCH_COUNT", matches
);
5184 reply("MSG_NO_MATCHES");
5188 static CHANSERV_FUNC(cmd_say
)
5194 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5195 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5197 else if(GetUserH(argv
[1]))
5200 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5201 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5205 reply("MSG_NOT_TARGET_NAME");
5211 static CHANSERV_FUNC(cmd_emote
)
5217 /* CTCP is so annoying. */
5218 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5219 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5221 else if(GetUserH(argv
[1]))
5223 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5224 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5228 reply("MSG_NOT_TARGET_NAME");
5234 struct channelList
*
5235 chanserv_support_channels(void)
5237 return &chanserv_conf
.support_channels
;
5240 static CHANSERV_FUNC(cmd_expire
)
5242 int channel_count
= registered_channels
;
5243 expire_channels(NULL
);
5244 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5249 chanserv_expire_suspension(void *data
)
5251 struct suspended
*suspended
= data
;
5252 struct chanNode
*channel
;
5254 if(!suspended
->expires
|| (now
< suspended
->expires
))
5255 suspended
->revoked
= now
;
5256 channel
= suspended
->cData
->channel
;
5257 suspended
->cData
->channel
= channel
;
5258 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5259 if(!IsOffChannel(suspended
->cData
))
5261 struct mod_chanmode change
;
5262 mod_chanmode_init(&change
);
5264 change
.args
[0].mode
= MODE_CHANOP
;
5265 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5266 mod_chanmode_announce(chanserv
, channel
, &change
);
5270 static CHANSERV_FUNC(cmd_csuspend
)
5272 struct suspended
*suspended
;
5273 char reason
[MAXLEN
];
5274 time_t expiry
, duration
;
5275 struct userData
*uData
;
5279 if(IsProtected(channel
->channel_info
))
5281 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5285 if(argv
[1][0] == '!')
5287 else if(IsSuspended(channel
->channel_info
))
5289 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5290 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5294 if(!strcmp(argv
[1], "0"))
5296 else if((duration
= ParseInterval(argv
[1])))
5297 expiry
= now
+ duration
;
5300 reply("MSG_INVALID_DURATION", argv
[1]);
5304 unsplit_string(argv
+ 2, argc
- 2, reason
);
5306 suspended
= calloc(1, sizeof(*suspended
));
5307 suspended
->revoked
= 0;
5308 suspended
->issued
= now
;
5309 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5310 suspended
->expires
= expiry
;
5311 suspended
->reason
= strdup(reason
);
5312 suspended
->cData
= channel
->channel_info
;
5313 suspended
->previous
= suspended
->cData
->suspended
;
5314 suspended
->cData
->suspended
= suspended
;
5316 if(suspended
->expires
)
5317 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5319 if(IsSuspended(channel
->channel_info
))
5321 suspended
->previous
->revoked
= now
;
5322 if(suspended
->previous
->expires
)
5323 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5324 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5325 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5329 /* Mark all users in channel as absent. */
5330 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5339 /* Mark the channel as suspended, then part. */
5340 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5341 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5342 reply("CSMSG_SUSPENDED", channel
->name
);
5343 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5344 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5349 static CHANSERV_FUNC(cmd_cunsuspend
)
5351 struct suspended
*suspended
;
5352 char message
[MAXLEN
];
5354 if(!IsSuspended(channel
->channel_info
))
5356 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5360 suspended
= channel
->channel_info
->suspended
;
5362 /* Expire the suspension and join ChanServ to the channel. */
5363 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5364 chanserv_expire_suspension(suspended
);
5365 reply("CSMSG_UNSUSPENDED", channel
->name
);
5366 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5367 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5371 typedef struct chanservSearch
5379 unsigned long flags
;
5383 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5386 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5391 search
= malloc(sizeof(struct chanservSearch
));
5392 memset(search
, 0, sizeof(*search
));
5395 for(i
= 0; i
< argc
; i
++)
5397 /* Assume all criteria require arguments. */
5400 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5404 if(!irccasecmp(argv
[i
], "name"))
5405 search
->name
= argv
[++i
];
5406 else if(!irccasecmp(argv
[i
], "registrar"))
5407 search
->registrar
= argv
[++i
];
5408 else if(!irccasecmp(argv
[i
], "unvisited"))
5409 search
->unvisited
= ParseInterval(argv
[++i
]);
5410 else if(!irccasecmp(argv
[i
], "registered"))
5411 search
->registered
= ParseInterval(argv
[++i
]);
5412 else if(!irccasecmp(argv
[i
], "flags"))
5415 if(!irccasecmp(argv
[i
], "nodelete"))
5416 search
->flags
|= CHANNEL_NODELETE
;
5417 else if(!irccasecmp(argv
[i
], "suspended"))
5418 search
->flags
|= CHANNEL_SUSPENDED
;
5421 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5425 else if(!irccasecmp(argv
[i
], "limit"))
5426 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5429 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5434 if(search
->name
&& !strcmp(search
->name
, "*"))
5436 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5437 search
->registrar
= 0;
5446 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5448 const char *name
= channel
->channel
->name
;
5449 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5450 (search
->registrar
&& !channel
->registrar
) ||
5451 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5452 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5453 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5454 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5461 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5463 struct chanData
*channel
;
5464 unsigned int matches
= 0;
5466 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5468 if(!chanserv_channel_match(channel
, search
))
5478 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5483 search_print(struct chanData
*channel
, void *data
)
5485 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5488 static CHANSERV_FUNC(cmd_search
)
5491 unsigned int matches
;
5492 channel_search_func action
;
5496 if(!irccasecmp(argv
[1], "count"))
5497 action
= search_count
;
5498 else if(!irccasecmp(argv
[1], "print"))
5499 action
= search_print
;
5502 reply("CSMSG_ACTION_INVALID", argv
[1]);
5506 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5510 if(action
== search_count
)
5511 search
->limit
= INT_MAX
;
5513 if(action
== search_print
)
5515 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5516 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5520 matches
= chanserv_channel_search(search
, action
, user
);
5523 reply("MSG_MATCH_COUNT", matches
);
5525 reply("MSG_NO_MATCHES");
5531 static CHANSERV_FUNC(cmd_unvisited
)
5533 struct chanData
*cData
;
5534 time_t interval
= chanserv_conf
.channel_expire_delay
;
5535 char buffer
[INTERVALLEN
];
5536 unsigned int limit
= 25, matches
= 0;
5540 interval
= ParseInterval(argv
[1]);
5542 limit
= atoi(argv
[2]);
5545 intervalString(buffer
, interval
, user
->handle_info
);
5546 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5548 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5550 if((now
- cData
->visited
) < interval
)
5553 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5554 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5561 static MODCMD_FUNC(chan_opt_defaulttopic
)
5567 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5569 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5573 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5575 free(channel
->channel_info
->topic
);
5576 if(topic
[0] == '*' && topic
[1] == 0)
5578 topic
= channel
->channel_info
->topic
= NULL
;
5582 topic
= channel
->channel_info
->topic
= strdup(topic
);
5583 if(channel
->channel_info
->topic_mask
5584 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5585 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5587 SetChannelTopic(channel
, chanserv
, chanserv
, topic
? topic
: "", 1);
5590 if(channel
->channel_info
->topic
)
5591 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5593 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5597 static MODCMD_FUNC(chan_opt_topicmask
)
5601 struct chanData
*cData
= channel
->channel_info
;
5604 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5606 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5610 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5612 if(cData
->topic_mask
)
5613 free(cData
->topic_mask
);
5614 if(mask
[0] == '*' && mask
[1] == 0)
5616 cData
->topic_mask
= 0;
5620 cData
->topic_mask
= strdup(mask
);
5622 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5623 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5624 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5628 if(channel
->channel_info
->topic_mask
)
5629 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5631 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5635 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5639 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5643 if(greeting
[0] == '*' && greeting
[1] == 0)
5647 unsigned int length
= strlen(greeting
);
5648 if(length
> chanserv_conf
.greeting_length
)
5650 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5653 *data
= strdup(greeting
);
5662 reply(name
, user_find_message(user
, "MSG_NONE"));
5666 static MODCMD_FUNC(chan_opt_greeting
)
5668 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5671 static MODCMD_FUNC(chan_opt_usergreeting
)
5673 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5676 static MODCMD_FUNC(chan_opt_modes
)
5678 struct mod_chanmode
*new_modes
;
5679 char modes
[MODELEN
];
5683 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5685 reply("CSMSG_NO_ACCESS");
5688 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5690 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5692 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5694 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5697 else if(new_modes
->argc
> 1)
5699 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5700 mod_chanmode_free(new_modes
);
5705 channel
->channel_info
->modes
= *new_modes
;
5706 modcmd_chanmode_announce(new_modes
);
5707 mod_chanmode_free(new_modes
);
5711 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5713 reply("CSMSG_SET_MODES", modes
);
5715 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5719 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5721 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5723 struct chanData
*cData
= channel
->channel_info
;
5728 /* Set flag according to value. */
5729 if(enabled_string(argv
[1]))
5731 cData
->flags
|= mask
;
5734 else if(disabled_string(argv
[1]))
5736 cData
->flags
&= ~mask
;
5741 reply("MSG_INVALID_BINARY", argv
[1]);
5747 /* Find current option value. */
5748 value
= (cData
->flags
& mask
) ? 1 : 0;
5752 reply(name
, user_find_message(user
, "MSG_ON"));
5754 reply(name
, user_find_message(user
, "MSG_OFF"));
5758 static MODCMD_FUNC(chan_opt_nodelete
)
5760 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5762 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5766 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5769 static MODCMD_FUNC(chan_opt_dynlimit
)
5771 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5774 static MODCMD_FUNC(chan_opt_offchannel
)
5776 struct chanData
*cData
= channel
->channel_info
;
5781 /* Set flag according to value. */
5782 if(enabled_string(argv
[1]))
5784 if(!IsOffChannel(cData
))
5785 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5786 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5789 else if(disabled_string(argv
[1]))
5791 if(IsOffChannel(cData
))
5793 struct mod_chanmode change
;
5794 mod_chanmode_init(&change
);
5796 change
.args
[0].mode
= MODE_CHANOP
;
5797 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5798 mod_chanmode_announce(chanserv
, channel
, &change
);
5800 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5805 reply("MSG_INVALID_BINARY", argv
[1]);
5811 /* Find current option value. */
5812 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5816 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5818 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5822 static MODCMD_FUNC(chan_opt_defaults
)
5824 struct userData
*uData
;
5825 struct chanData
*cData
;
5826 const char *confirm
;
5827 enum levelOption lvlOpt
;
5828 enum charOption chOpt
;
5830 cData
= channel
->channel_info
;
5831 uData
= GetChannelUser(cData
, user
->handle_info
);
5832 if(!uData
|| (uData
->access
< UL_OWNER
))
5834 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5837 confirm
= make_confirmation_string(uData
);
5838 if((argc
< 2) || strcmp(argv
[1], confirm
))
5840 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5843 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5844 cData
->modes
= chanserv_conf
.default_modes
;
5845 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5846 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5847 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5848 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5849 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5854 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5856 struct chanData
*cData
= channel
->channel_info
;
5857 struct userData
*uData
;
5858 unsigned short value
;
5862 if(!check_user_level(channel
, user
, option
, 1, 1))
5864 reply("CSMSG_CANNOT_SET");
5867 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5868 if(!value
&& strcmp(argv
[1], "0"))
5870 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5873 uData
= GetChannelUser(cData
, user
->handle_info
);
5874 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5876 reply("CSMSG_BAD_SETLEVEL");
5882 /* This test only applies to owners, since non-owners
5883 * trying to set an option to above their level get caught
5884 * by the CSMSG_BAD_SETLEVEL test above.
5886 if(value
> uData
->access
)
5888 reply("CSMSG_BAD_SETTERS");
5895 cData
->lvlOpts
[option
] = value
;
5897 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5901 static MODCMD_FUNC(chan_opt_enfops
)
5903 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5906 static MODCMD_FUNC(chan_opt_enfhalfops
)
5908 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5910 static MODCMD_FUNC(chan_opt_enfmodes
)
5912 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5915 static MODCMD_FUNC(chan_opt_enftopic
)
5917 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5920 static MODCMD_FUNC(chan_opt_pubcmd
)
5922 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5925 static MODCMD_FUNC(chan_opt_setters
)
5927 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5930 static MODCMD_FUNC(chan_opt_userinfo
)
5932 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5935 static MODCMD_FUNC(chan_opt_topicsnarf
)
5937 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5940 static MODCMD_FUNC(chan_opt_inviteme
)
5942 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5945 /* TODO: Make look like this when no args are
5947 * -X3- -------------------------------
5948 * -X3- BanTimeout: Bans are removed:
5949 * -X3- ----- * indicates current -----
5950 * -X3- 0: [*] Never.
5951 * -X3- 1: [ ] After 10 minutes.
5952 * -X3- 2: [ ] After 2 hours.
5953 * -X3- 3: [ ] After 4 hours.
5954 * -X3- 4: [ ] After 24 hours.
5955 * -X3- 5: [ ] After one week.
5956 * -X3- ------------- End -------------
5959 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5961 struct chanData
*cData
= channel
->channel_info
;
5962 int count
= charOptions
[option
].count
, index
;
5966 index
= atoi(argv
[1]);
5968 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5970 reply("CSMSG_INVALID_NUMERIC", index
);
5971 /* Show possible values. */
5972 for(index
= 0; index
< count
; index
++)
5973 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5977 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5981 /* Find current option value. */
5984 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5988 /* Somehow, the option value is corrupt; reset it to the default. */
5989 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5994 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5998 static MODCMD_FUNC(chan_opt_automode
)
6000 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
6003 static MODCMD_FUNC(chan_opt_protect
)
6005 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
6008 static MODCMD_FUNC(chan_opt_toys
)
6010 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
6013 static MODCMD_FUNC(chan_opt_ctcpreaction
)
6015 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
6018 static MODCMD_FUNC(chan_opt_bantimeout
)
6020 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
6023 static MODCMD_FUNC(chan_opt_topicrefresh
)
6025 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
6028 static struct svccmd_list set_shows_list
;
6031 handle_svccmd_unbind(struct svccmd
*target
) {
6033 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
6034 if(target
== set_shows_list
.list
[ii
])
6035 set_shows_list
.used
= 0;
6038 static CHANSERV_FUNC(cmd_set
)
6040 struct svccmd
*subcmd
;
6044 /* Check if we need to (re-)initialize set_shows_list. */
6045 if(!set_shows_list
.used
)
6047 if(!set_shows_list
.size
)
6049 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
6050 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
6052 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
6054 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
6055 sprintf(buf
, "%s %s", argv
[0], name
);
6056 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6059 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6062 svccmd_list_append(&set_shows_list
, subcmd
);
6068 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6069 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6071 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6073 subcmd
= set_shows_list
.list
[ii
];
6074 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6076 reply("CSMSG_CHANNEL_OPTIONS_END");
6080 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6081 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6084 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6087 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6089 reply("CSMSG_NO_ACCESS");
6093 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6097 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6099 struct userData
*uData
;
6101 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6104 reply("CSMSG_NOT_USER", channel
->name
);
6110 /* Just show current option value. */
6112 else if(enabled_string(argv
[1]))
6114 uData
->flags
|= mask
;
6116 else if(disabled_string(argv
[1]))
6118 uData
->flags
&= ~mask
;
6122 reply("MSG_INVALID_BINARY", argv
[1]);
6126 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6130 static MODCMD_FUNC(user_opt_autoop
)
6132 struct userData
*uData
;
6134 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6137 reply("CSMSG_NOT_USER", channel
->name
);
6140 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6141 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6143 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6144 /* TODO: add halfops error message? or is the op one generic enough? */
6147 static MODCMD_FUNC(user_opt_autoinvite
)
6149 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6152 static MODCMD_FUNC(user_opt_info
)
6154 struct userData
*uData
;
6157 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6161 /* If they got past the command restrictions (which require access)
6162 * but fail this test, we have some fool with security override on.
6164 reply("CSMSG_NOT_USER", channel
->name
);
6171 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6172 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6174 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6177 bp
= strcspn(infoline
, "\001");
6180 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6185 if(infoline
[0] == '*' && infoline
[1] == 0)
6188 uData
->info
= strdup(infoline
);
6191 reply("CSMSG_USET_INFO", uData
->info
);
6193 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6197 struct svccmd_list uset_shows_list
;
6199 static CHANSERV_FUNC(cmd_uset
)
6201 struct svccmd
*subcmd
;
6205 /* Check if we need to (re-)initialize uset_shows_list. */
6206 if(!uset_shows_list
.used
)
6210 "AutoOp", "AutoInvite", "Info"
6213 if(!uset_shows_list
.size
)
6215 uset_shows_list
.size
= ArrayLength(options
);
6216 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6218 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6220 const char *name
= options
[ii
];
6221 sprintf(buf
, "%s %s", argv
[0], name
);
6222 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6225 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6228 svccmd_list_append(&uset_shows_list
, subcmd
);
6234 /* Do this so options are presented in a consistent order. */
6235 reply("CSMSG_USER_OPTIONS");
6236 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6237 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6241 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6242 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6245 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6249 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6252 static CHANSERV_FUNC(cmd_giveownership
)
6254 struct handle_info
*new_owner_hi
;
6255 struct userData
*new_owner
, *curr_user
;
6256 struct chanData
*cData
= channel
->channel_info
;
6257 struct do_not_register
*dnr
;
6258 struct giveownership
*giveownership
;
6259 unsigned int force
, override
;
6260 unsigned short co_access
, new_owner_old_access
;
6261 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6264 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6265 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6267 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6268 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6269 && (uData
->access
> 500)
6270 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6271 || uData
->access
< 500));
6274 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6276 struct userData
*owner
= NULL
;
6277 for(curr_user
= channel
->channel_info
->users
;
6279 curr_user
= curr_user
->next
)
6281 if(curr_user
->access
!= UL_OWNER
)
6285 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6292 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6294 char delay
[INTERVALLEN
];
6295 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6296 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6299 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6301 if(new_owner_hi
== user
->handle_info
)
6303 reply("CSMSG_NO_TRANSFER_SELF");
6306 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6311 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6315 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6319 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6321 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6324 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6325 if(!IsHelping(user
))
6326 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6328 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6332 new_owner_old_access
= new_owner
->access
;
6333 if(new_owner
->access
>= UL_COOWNER
)
6334 co_access
= new_owner
->access
;
6336 co_access
= UL_COOWNER
;
6337 new_owner
->access
= UL_OWNER
;
6339 curr_user
->access
= co_access
;
6340 cData
->ownerTransfer
= now
;
6342 giveownership
= calloc(1, sizeof(*giveownership
));
6343 giveownership
->issued
= now
;
6344 giveownership
->old_owner
= curr_user
->handle
->handle
;
6345 giveownership
->target
= new_owner_hi
->handle
;
6346 giveownership
->target_access
= new_owner_old_access
;
6349 if(argc
> (2 + force
))
6351 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6352 giveownership
->reason
= strdup(transfer_reason
);
6354 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6357 giveownership
->previous
= channel
->channel_info
->giveownership
;
6358 channel
->channel_info
->giveownership
= giveownership
;
6360 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6361 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6362 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6367 chanserv_expire_user_suspension(void *data
)
6369 struct userData
*target
= data
;
6371 target
->expires
= 0;
6372 target
->flags
&= ~USER_SUSPENDED
;
6375 static CHANSERV_FUNC(cmd_suspend
)
6377 struct handle_info
*hi
;
6378 struct userData
*self
, *target
;
6382 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6383 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6384 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6386 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6389 if(target
->access
>= self
->access
)
6391 reply("MSG_USER_OUTRANKED", hi
->handle
);
6394 if(target
->flags
& USER_SUSPENDED
)
6396 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6401 target
->present
= 0;
6404 if(!strcmp(argv
[2], "0"))
6408 unsigned int duration
;
6409 if(!(duration
= ParseInterval(argv
[2])))
6411 reply("MSG_INVALID_DURATION", argv
[2]);
6414 expiry
= now
+ duration
;
6417 target
->expires
= expiry
;
6420 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6422 target
->flags
|= USER_SUSPENDED
;
6423 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6427 static CHANSERV_FUNC(cmd_unsuspend
)
6429 struct handle_info
*hi
;
6430 struct userData
*self
, *target
;
6433 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6434 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6435 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6437 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6440 if(target
->access
>= self
->access
)
6442 reply("MSG_USER_OUTRANKED", hi
->handle
);
6445 if(!(target
->flags
& USER_SUSPENDED
))
6447 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6450 target
->flags
&= ~USER_SUSPENDED
;
6451 scan_user_presence(target
, NULL
);
6452 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6453 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6457 static MODCMD_FUNC(cmd_deleteme
)
6459 struct handle_info
*hi
;
6460 struct userData
*target
;
6461 const char *confirm_string
;
6462 unsigned short access
;
6465 hi
= user
->handle_info
;
6466 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6468 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6471 if(target
->access
== UL_OWNER
)
6473 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6476 confirm_string
= make_confirmation_string(target
);
6477 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6479 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6482 access
= target
->access
;
6483 channel_name
= strdup(channel
->name
);
6484 del_channel_user(target
, 1);
6485 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6491 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6493 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6494 struct chanData
*cData
;
6497 for(cData
= channelList
; cData
; cData
= cData
->next
)
6499 if(IsSuspended(cData
))
6501 opt
= cData
->chOpts
[chTopicRefresh
];
6504 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6507 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6508 cData
->last_refresh
= refresh_num
;
6510 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6513 static CHANSERV_FUNC(cmd_unf
)
6517 char response
[MAXLEN
];
6518 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6519 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6520 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6523 reply("CSMSG_UNF_RESPONSE");
6527 static CHANSERV_FUNC(cmd_ping
)
6531 char response
[MAXLEN
];
6532 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6533 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6534 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6537 reply("CSMSG_PING_RESPONSE");
6541 static CHANSERV_FUNC(cmd_wut
)
6545 char response
[MAXLEN
];
6546 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6547 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6548 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6551 reply("CSMSG_WUT_RESPONSE");
6556 static CHANSERV_FUNC(cmd_8ball
)
6558 unsigned int i
, j
, accum
;
6563 for(i
=1; i
<argc
; i
++)
6564 for(j
=0; argv
[i
][j
]; j
++)
6565 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6566 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6569 char response
[MAXLEN
];
6570 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6571 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6574 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6578 #else /* Use cool 8ball instead */
6580 void eightball(char *outcome
, int method
, unsigned int seed
)
6584 #define NUMOFCOLORS 18
6585 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6586 "white", "black", "grey", "brown",
6587 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6588 "fuchsia","turquoise","magenta", "cyan"};
6589 #define NUMOFLOCATIONS 50
6590 char balllocations
[50][55] = {
6591 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6592 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6593 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6594 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6595 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6596 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6597 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6598 "your bra", "your hair", "your bed", "the couch", "the wall",
6599 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6600 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6601 #define NUMOFPREPS 15
6602 char ballpreps
[50][50] = {
6603 "Near", "Somewhere near", "In", "In", "In",
6604 "In", "Hiding in", "Under", "Next to", "Over",
6605 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6606 #define NUMOFNUMS 34
6607 char ballnums
[50][50] = {
6608 "A hundred", "A thousand", "A few", "42",
6609 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6610 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6611 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6613 #define NUMOFMULTS 8
6614 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6617 * 0: normal (Not used in x3)
6624 if (method
== 1) /* A Color */
6628 answer
= (rand() % 12); /* Make sure this is the # of entries */
6631 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6633 case 1: strcpy(tmp
, "Sort of a light %s color.");
6635 case 2: strcpy(tmp
, "Dark and dreary %s.");
6637 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6639 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6641 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6643 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6645 case 10: strcpy(tmp
, "Solid %s.");
6647 case 11: strcpy(tmp
, "Transparent %s.");
6649 default: strcpy(outcome
, "An invalid random number was generated.");
6652 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6655 else if (method
== 2) /* Location */
6657 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6659 else if (method
== 3) /* Number of ___ */
6661 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6665 //Debug(DBGWARNING, "Error in 8ball.");
6670 static CHANSERV_FUNC(cmd_8ball
)
6672 char *word1
, *word2
, *word3
;
6673 static char eb
[MAXLEN
];
6674 unsigned int accum
, i
, j
;
6678 for(i
=1; i
<argc
; i
++)
6679 for(j
=0; argv
[i
][j
]; j
++)
6680 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6682 accum
+= time(NULL
)/3600;
6684 word2
= argc
>2?argv
[2]:"";
6685 word3
= argc
>3?argv
[3]:"";
6688 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6689 eightball(eb
, 1, accum
);
6690 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6691 eightball(eb
, 1, accum
);
6692 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6693 eightball(eb
, 1, accum
);
6694 /*** LOCATION *****/
6699 (strcasecmp(word1
, "where") == 0) &&
6700 (strcasecmp(word2
, "is") == 0)
6704 strcasecmp(word1
, "where's") == 0
6707 eightball(eb
, 2, accum
);
6709 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6710 eightball(eb
, 3, accum
);
6714 /* Generic 8ball question.. so pull from x3.conf srvx style */
6717 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6720 char response
[MAXLEN
];
6721 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6722 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6725 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6731 char response
[MAXLEN
];
6732 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6733 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6736 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6741 static CHANSERV_FUNC(cmd_d
)
6743 unsigned long sides
, count
, modifier
, ii
, total
;
6744 char response
[MAXLEN
], *sep
;
6748 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6758 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6759 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6763 else if((sep
[0] == '-') && isdigit(sep
[1]))
6764 modifier
= strtoul(sep
, NULL
, 10);
6765 else if((sep
[0] == '+') && isdigit(sep
[1]))
6766 modifier
= strtoul(sep
+1, NULL
, 10);
6773 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6778 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6781 for(total
= ii
= 0; ii
< count
; ++ii
)
6782 total
+= (rand() % sides
) + 1;
6785 if((count
> 1) || modifier
)
6787 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6788 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6792 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6793 sprintf(response
, fmt
, total
, sides
);
6796 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6798 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6802 static CHANSERV_FUNC(cmd_huggle
)
6804 /* CTCP must be via PRIVMSG, never notice */
6806 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6808 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6812 static CHANSERV_FUNC(cmd_calc
)
6814 char response
[MAXLEN
];
6817 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6820 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6822 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6827 chanserv_adjust_limit(void *data
)
6829 struct mod_chanmode change
;
6830 struct chanData
*cData
= data
;
6831 struct chanNode
*channel
= cData
->channel
;
6834 if(IsSuspended(cData
))
6837 cData
->limitAdjusted
= now
;
6838 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6839 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6841 if(limit
> cData
->modes
.new_limit
)
6842 limit
= cData
->modes
.new_limit
;
6843 else if(limit
== cData
->modes
.new_limit
)
6847 mod_chanmode_init(&change
);
6848 change
.modes_set
= MODE_LIMIT
;
6849 change
.new_limit
= limit
;
6850 mod_chanmode_announce(chanserv
, channel
, &change
);
6854 handle_new_channel(struct chanNode
*channel
)
6856 struct chanData
*cData
;
6858 if(!(cData
= channel
->channel_info
))
6861 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6862 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6864 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6865 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6868 /* Welcome to my worst nightmare. Warning: Read (or modify)
6869 the code below at your own risk. */
6871 handle_join(struct modeNode
*mNode
)
6873 struct mod_chanmode change
;
6874 struct userNode
*user
= mNode
->user
;
6875 struct chanNode
*channel
= mNode
->channel
;
6876 struct chanData
*cData
;
6877 struct userData
*uData
= NULL
;
6878 struct banData
*bData
;
6879 struct handle_info
*handle
;
6880 unsigned int modes
= 0, info
= 0;
6883 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6886 cData
= channel
->channel_info
;
6887 if(channel
->members
.used
> cData
->max
)
6888 cData
->max
= channel
->members
.used
;
6891 /* Check for bans. If they're joining through a ban, one of two
6893 * 1: Join during a netburst, by riding the break. Kick them
6894 * unless they have ops or voice in the channel.
6895 * 2: They're allowed to join through the ban (an invite in
6896 * ircu2.10, or a +e on Hybrid, or something).
6897 * If they're not joining through a ban, and the banlist is not
6898 * full, see if they're on the banlist for the channel. If so,
6901 if(user
->uplink
->burst
&& !mNode
->modes
)
6904 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
6906 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
6908 /* Riding a netburst. Naughty. */
6909 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
6916 mod_chanmode_init(&change
);
6918 if(channel
->banlist
.used
< MAXBANS
)
6920 /* Not joining through a ban. */
6921 for(bData
= cData
->bans
;
6922 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
6923 bData
= bData
->next
);
6927 char kick_reason
[MAXLEN
];
6928 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6930 bData
->triggered
= now
;
6931 if(bData
!= cData
->bans
)
6933 /* Shuffle the ban to the head of the list. */
6935 bData
->next
->prev
= bData
->prev
;
6937 bData
->prev
->next
= bData
->next
;
6940 bData
->next
= cData
->bans
;
6943 cData
->bans
->prev
= bData
;
6944 cData
->bans
= bData
;
6947 change
.args
[0].mode
= MODE_BAN
;
6948 change
.args
[0].u
.hostmask
= bData
->mask
;
6949 mod_chanmode_announce(chanserv
, channel
, &change
);
6950 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6955 /* ChanServ will not modify the limits in join-flooded channels.
6956 It will also skip DynLimit processing when the user (or srvx)
6957 is bursting in, because there are likely more incoming. */
6958 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6959 && !user
->uplink
->burst
6960 && !channel
->join_flooded
6961 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6963 /* The user count has begun "bumping" into the channel limit,
6964 so set a timer to raise the limit a bit. Any previous
6965 timers are removed so three incoming users within the delay
6966 results in one limit change, not three. */
6968 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6969 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6972 /* Give automodes exept during join-floods */
6973 if(!channel
->join_flooded
)
6975 if(cData
->chOpts
[chAutomode
] == 'v')
6976 modes
|= MODE_VOICE
;
6977 else if(cData
->chOpts
[chAutomode
] == 'h')
6978 modes
|= MODE_HALFOP
;
6979 else if(cData
->chOpts
[chAutomode
] == 'o')
6980 modes
|= MODE_CHANOP
;
6983 greeting
= cData
->greeting
;
6984 if(user
->handle_info
)
6986 handle
= user
->handle_info
;
6988 if(IsHelper(user
) && !IsHelping(user
))
6991 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6993 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6995 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7001 uData
= GetTrueChannelAccess(cData
, handle
);
7002 if(uData
&& !IsUserSuspended(uData
))
7004 /* non users getting automodes are handled above. */
7005 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
7007 if(uData
->access
>= UL_OP
)
7008 modes
|= MODE_CHANOP
;
7009 else if(uData
->access
>= UL_HALFOP
)
7010 modes
|= MODE_HALFOP
;
7011 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
7012 modes
|= MODE_VOICE
;
7014 if(uData
->access
>= UL_PRESENT
)
7015 cData
->visited
= now
;
7016 if(cData
->user_greeting
)
7017 greeting
= cData
->user_greeting
;
7019 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
7020 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
7028 /* If user joining normally (not during burst), apply op or voice,
7029 * and send greeting/userinfo as appropriate.
7031 if(!user
->uplink
->burst
)
7035 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7036 if(modes & MODE_CHANOP) {
7037 modes &= ~MODE_HALFOP;
7038 modes &= ~MODE_VOICE;
7041 change
.args
[0].mode
= modes
;
7042 change
.args
[0].u
.member
= mNode
;
7043 mod_chanmode_announce(chanserv
, channel
, &change
);
7046 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
7048 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
7054 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
7056 struct mod_chanmode change
;
7057 struct userData
*channel
;
7058 unsigned int ii
, jj
, i
;
7060 if(!user
->handle_info
)
7063 mod_chanmode_init(&change
);
7065 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7067 struct chanNode
*cn
;
7068 struct modeNode
*mn
;
7069 if(IsUserSuspended(channel
)
7070 || IsSuspended(channel
->channel
)
7071 || !(cn
= channel
->channel
->channel
))
7074 mn
= GetUserMode(cn
, user
);
7077 if(!IsUserSuspended(channel
)
7078 && IsUserAutoInvite(channel
)
7079 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7081 && !user
->uplink
->burst
)
7082 irc_invite(chanserv
, user
, cn
);
7086 if(channel
->access
>= UL_PRESENT
)
7087 channel
->channel
->visited
= now
;
7089 if(IsUserAutoOp(channel
))
7091 if(channel
->access
>= UL_OP
)
7092 change
.args
[0].mode
= MODE_CHANOP
;
7093 else if(channel
->access
>= UL_HALFOP
)
7094 change
.args
[0].mode
= MODE_HALFOP
;
7095 else if(channel
->access
>= UL_PEON
)
7096 change
.args
[0].mode
= MODE_VOICE
;
7098 change
.args
[0].mode
= 0;
7099 change
.args
[0].u
.member
= mn
;
7100 if(change
.args
[0].mode
)
7101 mod_chanmode_announce(chanserv
, cn
, &change
);
7104 channel
->seen
= now
;
7105 channel
->present
= 1;
7108 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7110 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7111 struct banData
*ban
;
7113 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7114 || !channel
->channel_info
7115 || IsSuspended(channel
->channel_info
))
7117 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7118 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7120 if(jj
< channel
->banlist
.used
)
7122 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7124 char kick_reason
[MAXLEN
];
7125 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7127 change
.args
[0].mode
= MODE_BAN
;
7128 change
.args
[0].u
.hostmask
= ban
->mask
;
7129 mod_chanmode_announce(chanserv
, channel
, &change
);
7130 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7131 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7132 ban
->triggered
= now
;
7137 if(IsSupportHelper(user
))
7139 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7141 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7143 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7149 if (user
->handle_info
->ignores
->used
) {
7150 for (i
=0; i
< user
->handle_info
->ignores
->used
; i
++) {
7151 irc_silence(user
, user
->handle_info
->ignores
->list
[i
], 1);
7157 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7159 struct chanData
*cData
;
7160 struct userData
*uData
;
7162 cData
= mn
->channel
->channel_info
;
7163 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7166 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7168 /* Allow for a bit of padding so that the limit doesn't
7169 track the user count exactly, which could get annoying. */
7170 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7172 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7173 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7177 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7179 scan_user_presence(uData
, mn
->user
);
7183 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7185 unsigned int ii
, jj
;
7186 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7188 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7189 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7191 if(jj
< mn
->user
->channels
.used
)
7194 if(ii
== chanserv_conf
.support_channels
.used
)
7195 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7200 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7202 struct userData
*uData
;
7204 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7205 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7206 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7209 if(protect_user(victim
, kicker
, channel
->channel_info
))
7211 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7212 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7215 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7220 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7222 struct chanData
*cData
;
7224 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7227 cData
= channel
->channel_info
;
7228 if(bad_topic(channel
, user
, channel
->topic
))
7229 { /* User doesnt have privs to set topics. Undo it */
7230 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7231 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7234 /* If there is a topic mask set, and the new topic doesnt match,
7235 * set the topic to mask + new_topic */
7236 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7238 char new_topic
[TOPICLEN
+1];
7239 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7242 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7243 /* and fall through to topicsnarf code below.. */
7245 else /* Topic couldnt fit into mask, was too long */
7247 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7248 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7249 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7253 /* With topicsnarf, grab the topic and save it as the default topic. */
7254 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7257 cData
->topic
= strdup(channel
->topic
);
7263 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7265 struct mod_chanmode
*bounce
= NULL
;
7266 unsigned int bnc
, ii
;
7269 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7272 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7273 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7275 char correct
[MAXLEN
];
7276 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7277 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7278 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7280 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7282 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7284 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7285 if(!protect_user(victim
, user
, channel
->channel_info
))
7288 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7291 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7292 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7293 if(bounce
->args
[bnc
].u
.member
)
7297 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7298 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7300 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7302 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7304 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7305 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7308 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7309 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7310 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7313 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7315 const char *ban
= change
->args
[ii
].u
.hostmask
;
7316 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7319 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7320 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7321 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7323 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7328 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7329 mod_chanmode_announce(chanserv
, channel
, bounce
);
7330 for(ii
= 0; ii
< change
->argc
; ++ii
)
7331 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7332 free((char*)bounce
->args
[ii
].u
.hostmask
);
7333 mod_chanmode_free(bounce
);
7338 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7340 struct chanNode
*channel
;
7341 struct banData
*bData
;
7342 struct mod_chanmode change
;
7343 unsigned int ii
, jj
;
7344 char kick_reason
[MAXLEN
];
7346 mod_chanmode_init(&change
);
7348 change
.args
[0].mode
= MODE_BAN
;
7349 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7351 channel
= user
->channels
.list
[ii
]->channel
;
7352 /* Need not check for bans if they're opped or voiced. */
7353 /* TODO: does this make sense in automode v, h, and o? *
7354 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7355 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7357 /* Need not check for bans unless channel registration is active. */
7358 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7360 /* Look for a matching ban already on the channel. */
7361 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7362 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7364 /* Need not act if we found one. */
7365 if(jj
< channel
->banlist
.used
)
7367 /* Look for a matching ban in this channel. */
7368 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7370 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7372 change
.args
[0].u
.hostmask
= bData
->mask
;
7373 mod_chanmode_announce(chanserv
, channel
, &change
);
7374 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7375 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7376 bData
->triggered
= now
;
7377 break; /* we don't need to check any more bans in the channel */
7382 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7384 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7388 dict_remove2(handle_dnrs
, old_handle
, 1);
7389 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7390 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7395 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7397 struct userNode
*h_user
;
7399 if(handle
->channels
)
7401 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7402 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7404 while(handle
->channels
)
7405 del_channel_user(handle
->channels
, 1);
7410 handle_server_link(UNUSED_ARG(struct server
*server
))
7412 struct chanData
*cData
;
7414 for(cData
= channelList
; cData
; cData
= cData
->next
)
7416 if(!IsSuspended(cData
))
7417 cData
->may_opchan
= 1;
7418 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7419 && !cData
->channel
->join_flooded
7420 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7421 < chanserv_conf
.adjust_threshold
))
7423 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7424 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7430 chanserv_conf_read(void)
7434 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7435 struct mod_chanmode
*change
;
7436 struct string_list
*strlist
;
7437 struct chanNode
*chan
;
7440 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7442 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7445 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7446 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7447 chanserv_conf
.support_channels
.used
= 0;
7448 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7450 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7452 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7455 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7457 channelList_append(&chanserv_conf
.support_channels
, chan
);
7460 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7463 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7466 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7468 channelList_append(&chanserv_conf
.support_channels
, chan
);
7470 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7471 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7472 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7473 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7474 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7475 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7476 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7477 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7478 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7479 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7480 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7481 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7482 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7483 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7484 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7485 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7486 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7487 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7488 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7489 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7490 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7491 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7492 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7493 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7494 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7496 NickChange(chanserv
, str
, 0);
7497 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7498 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7499 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7500 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7501 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7502 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7503 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7504 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7505 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7506 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7507 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7508 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7509 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7510 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7511 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7512 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7513 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7516 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7517 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7518 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7519 && (change
->argc
< 2))
7521 chanserv_conf
.default_modes
= *change
;
7522 mod_chanmode_free(change
);
7524 free_string_list(chanserv_conf
.set_shows
);
7525 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7527 strlist
= string_list_copy(strlist
);
7530 static const char *list
[] = {
7531 /* free form text */
7532 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7533 /* options based on user level */
7534 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7535 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7536 /* multiple choice options */
7537 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7538 /* binary options */
7539 "DynLimit", "NoDelete", "BanTimeout",
7544 strlist
= alloc_string_list(ArrayLength(list
)-1);
7545 for(ii
=0; list
[ii
]; ii
++)
7546 string_list_append(strlist
, strdup(list
[ii
]));
7548 chanserv_conf
.set_shows
= strlist
;
7549 /* We don't look things up now, in case the list refers to options
7550 * defined by modules initialized after this point. Just mark the
7551 * function list as invalid, so it will be initialized.
7553 set_shows_list
.used
= 0;
7554 free_string_list(chanserv_conf
.eightball
);
7555 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7558 strlist
= string_list_copy(strlist
);
7562 strlist
= alloc_string_list(4);
7563 string_list_append(strlist
, strdup("Yes."));
7564 string_list_append(strlist
, strdup("No."));
7565 string_list_append(strlist
, strdup("Maybe so."));
7567 chanserv_conf
.eightball
= strlist
;
7568 free_string_list(chanserv_conf
.old_ban_names
);
7569 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7571 strlist
= string_list_copy(strlist
);
7573 strlist
= alloc_string_list(2);
7574 chanserv_conf
.old_ban_names
= strlist
;
7575 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7576 off_channel
= str
? atoi(str
) : 0;
7580 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7583 struct note_type
*ntype
;
7586 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7588 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7591 if(!(ntype
= chanserv_create_note_type(key
)))
7593 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7597 /* Figure out set access */
7598 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7600 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7601 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7603 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7605 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7606 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7608 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7610 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7614 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7615 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7616 ntype
->set_access
.min_opserv
= 0;
7619 /* Figure out visibility */
7620 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7621 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7622 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7623 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7624 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7625 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7626 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7627 ntype
->visible_type
= NOTE_VIS_ALL
;
7629 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7631 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7632 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7636 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7638 struct handle_info
*handle
;
7639 struct userData
*uData
;
7640 char *seen
, *inf
, *flags
, *expires
;
7642 unsigned short access
;
7644 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7646 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7650 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7651 if(access
> UL_OWNER
)
7653 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7657 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7658 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7659 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7660 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7661 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7662 handle
= get_handle_info(key
);
7665 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7669 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7670 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7671 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7673 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7675 if(uData
->expires
> now
)
7676 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7678 uData
->flags
&= ~USER_SUSPENDED
;
7681 /* Upgrade: set autoop to the inverse of noautoop */
7682 if(chanserv_read_version
< 2)
7684 /* if noautoop is true, set autoop false, and vice versa */
7685 if(uData
->flags
& USER_NOAUTO_OP
)
7686 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7688 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7689 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
);
7695 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7697 struct banData
*bData
;
7698 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7699 time_t set_time
, triggered_time
, expires_time
;
7701 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7703 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7707 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7708 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7709 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7710 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7711 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7712 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7713 if (!reason
|| !owner
)
7716 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7717 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7719 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7721 expires_time
= set_time
+ atoi(s_duration
);
7725 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7728 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7731 static struct suspended
*
7732 chanserv_read_suspended(dict_t obj
)
7734 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7738 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7739 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7740 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7741 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7742 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7743 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7744 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7745 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7746 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7747 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7751 static struct giveownership
*
7752 chanserv_read_giveownership(dict_t obj
)
7754 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7758 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7759 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7761 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7763 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7764 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7766 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7767 giveownership
->reason
= str
? strdup(str
) : NULL
;
7768 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7769 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7771 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7772 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7773 return giveownership
;
7777 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7779 struct suspended
*suspended
;
7780 struct giveownership
*giveownership
;
7781 struct mod_chanmode
*modes
;
7782 struct chanNode
*cNode
;
7783 struct chanData
*cData
;
7784 struct dict
*channel
, *obj
;
7785 char *str
, *argv
[10];
7789 channel
= hir
->d
.object
;
7791 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7794 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7797 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7800 cData
= register_channel(cNode
, str
);
7803 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7807 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7809 enum levelOption lvlOpt
;
7810 enum charOption chOpt
;
7812 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7813 cData
->flags
= atoi(str
);
7815 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7817 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7819 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7820 else if(levelOptions
[lvlOpt
].old_flag
)
7822 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7823 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7825 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7829 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7831 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7833 cData
->chOpts
[chOpt
] = str
[0];
7836 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7838 enum levelOption lvlOpt
;
7839 enum charOption chOpt
;
7842 cData
->flags
= base64toint(str
, 5);
7843 count
= strlen(str
+= 5);
7844 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7847 if(levelOptions
[lvlOpt
].old_flag
)
7849 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7850 lvl
= levelOptions
[lvlOpt
].flag_value
;
7852 lvl
= levelOptions
[lvlOpt
].default_value
;
7854 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7856 case 'c': lvl
= UL_COOWNER
; break;
7857 case 'm': lvl
= UL_MANAGER
; break;
7858 case 'n': lvl
= UL_OWNER
+1; break;
7859 case 'o': lvl
= UL_OP
; break;
7860 case 'p': lvl
= UL_PEON
; break;
7861 case 'h': lvl
= UL_HALFOP
; break;
7862 case 'w': lvl
= UL_OWNER
; break;
7863 default: lvl
= 0; break;
7865 cData
->lvlOpts
[lvlOpt
] = lvl
;
7867 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7868 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7871 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7873 suspended
= chanserv_read_suspended(obj
);
7874 cData
->suspended
= suspended
;
7875 suspended
->cData
= cData
;
7876 /* We could use suspended->expires and suspended->revoked to
7877 * set the CHANNEL_SUSPENDED flag, but we don't. */
7879 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7881 suspended
= calloc(1, sizeof(*suspended
));
7882 suspended
->issued
= 0;
7883 suspended
->revoked
= 0;
7884 suspended
->suspender
= strdup(str
);
7885 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7886 suspended
->expires
= str
? atoi(str
) : 0;
7887 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7888 suspended
->reason
= strdup(str
? str
: "No reason");
7889 suspended
->previous
= NULL
;
7890 cData
->suspended
= suspended
;
7891 suspended
->cData
= cData
;
7895 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7896 suspended
= NULL
; /* to squelch a warning */
7899 if(IsSuspended(cData
)) {
7900 if(suspended
->expires
> now
)
7901 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7902 else if(suspended
->expires
)
7903 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7906 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
7908 giveownership
= chanserv_read_giveownership(obj
);
7909 cData
->giveownership
= giveownership
;
7912 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7913 struct mod_chanmode change
;
7914 mod_chanmode_init(&change
);
7916 change
.args
[0].mode
= MODE_CHANOP
;
7917 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7918 mod_chanmode_announce(chanserv
, cNode
, &change
);
7921 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7922 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7923 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7924 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7925 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7926 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7927 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7928 cData
->max
= str
? atoi(str
) : 0;
7929 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7930 cData
->greeting
= str
? strdup(str
) : NULL
;
7931 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7932 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7933 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7934 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7935 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7936 cData
->topic
= str
? strdup(str
) : NULL
;
7938 if(!IsSuspended(cData
)
7939 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7940 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7941 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
7942 cData
->modes
= *modes
;
7944 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7945 if(cData
->modes
.argc
> 1)
7946 cData
->modes
.argc
= 1;
7947 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7948 mod_chanmode_free(modes
);
7951 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7952 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7953 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7955 if(!cData
->users
&& !IsProtected(cData
))
7957 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7958 unregister_channel(cData
, "has empty user list.");
7962 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7963 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7964 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7966 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7967 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7969 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7970 struct record_data
*rd
= iter_data(it
);
7971 const char *note
, *setter
;
7973 if(rd
->type
!= RECDB_OBJECT
)
7975 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7979 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7981 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7983 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7987 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7988 if(!setter
) setter
= "<unknown>";
7989 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7997 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7999 const char *setter
, *reason
, *str
;
8000 struct do_not_register
*dnr
;
8002 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
8005 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
8008 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
8011 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
8014 dnr
= chanserv_add_dnr(key
, setter
, reason
);
8017 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
8019 dnr
->set
= atoi(str
);
8025 chanserv_version_read(struct dict
*section
)
8029 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
8031 chanserv_read_version
= atoi(str
);
8032 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
8036 chanserv_saxdb_read(struct dict
*database
)
8038 struct dict
*section
;
8041 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
8042 chanserv_version_read(section
);
8044 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
8045 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8046 chanserv_note_type_read(iter_key(it
), iter_data(it
));
8048 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
8049 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8050 chanserv_channel_read(iter_key(it
), iter_data(it
));
8052 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
8053 for(it
= dict_first(section
); it
; it
= iter_next(it
))
8054 chanserv_dnr_read(iter_key(it
), iter_data(it
));
8060 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
8062 int high_present
= 0;
8063 saxdb_start_record(ctx
, KEY_USERS
, 1);
8064 for(; uData
; uData
= uData
->next
)
8066 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8068 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8069 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8070 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8072 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8074 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8076 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8077 saxdb_end_record(ctx
);
8079 saxdb_end_record(ctx
);
8080 return high_present
;
8084 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8088 saxdb_start_record(ctx
, KEY_BANS
, 1);
8089 for(; bData
; bData
= bData
->next
)
8091 saxdb_start_record(ctx
, bData
->mask
, 0);
8092 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8093 if(bData
->triggered
)
8094 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8096 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8098 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8100 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8101 saxdb_end_record(ctx
);
8103 saxdb_end_record(ctx
);
8107 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8109 saxdb_start_record(ctx
, name
, 0);
8110 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8111 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8113 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8115 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8117 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8119 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8120 saxdb_end_record(ctx
);
8124 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8126 saxdb_start_record(ctx
, name
, 0);
8127 if(giveownership
->staff_issuer
)
8128 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8129 if(giveownership
->old_owner
)
8130 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8131 if(giveownership
->target
)
8132 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8133 if(giveownership
->target_access
)
8134 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8135 if(giveownership
->reason
)
8136 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8137 if(giveownership
->issued
)
8138 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8139 if(giveownership
->previous
)
8140 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8141 saxdb_end_record(ctx
);
8145 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8149 enum levelOption lvlOpt
;
8150 enum charOption chOpt
;
8152 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8154 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8155 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8157 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8158 if(channel
->registrar
)
8159 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8160 if(channel
->greeting
)
8161 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8162 if(channel
->user_greeting
)
8163 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8164 if(channel
->topic_mask
)
8165 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8166 if(channel
->suspended
)
8167 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8168 if(channel
->giveownership
)
8169 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8171 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8172 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8173 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8174 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8175 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8177 buf
[0] = channel
->chOpts
[chOpt
];
8179 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8181 saxdb_end_record(ctx
);
8183 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8185 mod_chanmode_format(&channel
->modes
, buf
);
8186 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8189 high_present
= chanserv_write_users(ctx
, channel
->users
);
8190 chanserv_write_bans(ctx
, channel
->bans
);
8192 if(dict_size(channel
->notes
))
8196 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8197 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8199 struct note
*note
= iter_data(it
);
8200 saxdb_start_record(ctx
, iter_key(it
), 0);
8201 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8202 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8203 saxdb_end_record(ctx
);
8205 saxdb_end_record(ctx
);
8208 if(channel
->ownerTransfer
)
8209 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8210 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8211 saxdb_end_record(ctx
);
8215 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8219 saxdb_start_record(ctx
, ntype
->name
, 0);
8220 switch(ntype
->set_access_type
)
8222 case NOTE_SET_CHANNEL_ACCESS
:
8223 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8225 case NOTE_SET_CHANNEL_SETTER
:
8226 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8228 case NOTE_SET_PRIVILEGED
: default:
8229 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8232 switch(ntype
->visible_type
)
8234 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8235 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8236 case NOTE_VIS_PRIVILEGED
:
8237 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8239 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8240 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8241 saxdb_end_record(ctx
);
8245 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8247 struct do_not_register
*dnr
;
8250 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8252 dnr
= iter_data(it
);
8253 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8255 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8256 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8257 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8258 saxdb_end_record(ctx
);
8263 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8266 struct chanData
*channel
;
8268 /* Version Control*/
8269 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8270 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8271 saxdb_end_record(ctx
);
8274 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8275 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8276 chanserv_write_note_type(ctx
, iter_data(it
));
8277 saxdb_end_record(ctx
);
8280 saxdb_start_record(ctx
, KEY_DNR
, 1);
8281 write_dnrs_helper(ctx
, handle_dnrs
);
8282 write_dnrs_helper(ctx
, plain_dnrs
);
8283 write_dnrs_helper(ctx
, mask_dnrs
);
8284 saxdb_end_record(ctx
);
8287 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8288 for(channel
= channelList
; channel
; channel
= channel
->next
)
8289 chanserv_write_channel(ctx
, channel
);
8290 saxdb_end_record(ctx
);
8296 chanserv_db_cleanup(void) {
8298 unreg_part_func(handle_part
);
8300 unregister_channel(channelList
, "terminating.");
8301 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8302 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8303 free(chanserv_conf
.support_channels
.list
);
8304 dict_delete(handle_dnrs
);
8305 dict_delete(plain_dnrs
);
8306 dict_delete(mask_dnrs
);
8307 dict_delete(note_types
);
8308 free_string_list(chanserv_conf
.eightball
);
8309 free_string_list(chanserv_conf
.old_ban_names
);
8310 free_string_list(chanserv_conf
.set_shows
);
8311 free(set_shows_list
.list
);
8312 free(uset_shows_list
.list
);
8315 struct userData
*helper
= helperList
;
8316 helperList
= helperList
->next
;
8321 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8322 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8323 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8326 init_chanserv(const char *nick
)
8328 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8329 conf_register_reload(chanserv_conf_read
);
8331 reg_server_link_func(handle_server_link
);
8333 reg_new_channel_func(handle_new_channel
);
8334 reg_join_func(handle_join
);
8335 reg_part_func(handle_part
);
8336 reg_kick_func(handle_kick
);
8337 reg_topic_func(handle_topic
);
8338 reg_mode_change_func(handle_mode
);
8339 reg_nick_change_func(handle_nick_change
);
8341 reg_auth_func(handle_auth
);
8342 reg_handle_rename_func(handle_rename
);
8343 reg_unreg_func(handle_unreg
);
8345 handle_dnrs
= dict_new();
8346 dict_set_free_data(handle_dnrs
, free
);
8347 plain_dnrs
= dict_new();
8348 dict_set_free_data(plain_dnrs
, free
);
8349 mask_dnrs
= dict_new();
8350 dict_set_free_data(mask_dnrs
, free
);
8352 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8353 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8354 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8355 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8356 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8357 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8358 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8359 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8360 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8361 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8363 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8365 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8366 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8368 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8369 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8370 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8371 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8372 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8374 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8375 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8376 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8377 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8378 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8379 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8381 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8382 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8383 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8384 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8386 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8387 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8388 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8389 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8390 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8391 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8392 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8393 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8394 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8395 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8397 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8398 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8399 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8400 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8401 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8402 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8403 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8404 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8405 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8406 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8407 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8408 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8409 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8410 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8412 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8413 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8414 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8415 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8417 /* if you change dellamer access, see also places
8418 * like unbanme which have manager hardcoded. */
8419 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8420 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8422 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8424 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8426 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8427 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8428 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8429 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8430 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8431 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8432 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8433 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8434 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8435 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8436 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8437 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8439 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8440 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8442 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8443 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8444 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8445 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8447 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8448 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8449 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8450 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8451 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8453 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8454 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8455 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8456 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8457 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8458 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8459 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8461 /* Channel options */
8462 DEFINE_CHANNEL_OPTION(defaulttopic
);
8463 DEFINE_CHANNEL_OPTION(topicmask
);
8464 DEFINE_CHANNEL_OPTION(greeting
);
8465 DEFINE_CHANNEL_OPTION(usergreeting
);
8466 DEFINE_CHANNEL_OPTION(modes
);
8467 DEFINE_CHANNEL_OPTION(enfops
);
8468 DEFINE_CHANNEL_OPTION(enfhalfops
);
8469 DEFINE_CHANNEL_OPTION(automode
);
8470 DEFINE_CHANNEL_OPTION(protect
);
8471 DEFINE_CHANNEL_OPTION(enfmodes
);
8472 DEFINE_CHANNEL_OPTION(enftopic
);
8473 DEFINE_CHANNEL_OPTION(pubcmd
);
8474 DEFINE_CHANNEL_OPTION(userinfo
);
8475 DEFINE_CHANNEL_OPTION(dynlimit
);
8476 DEFINE_CHANNEL_OPTION(topicsnarf
);
8477 DEFINE_CHANNEL_OPTION(nodelete
);
8478 DEFINE_CHANNEL_OPTION(toys
);
8479 DEFINE_CHANNEL_OPTION(setters
);
8480 DEFINE_CHANNEL_OPTION(topicrefresh
);
8481 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8482 DEFINE_CHANNEL_OPTION(bantimeout
);
8483 DEFINE_CHANNEL_OPTION(inviteme
);
8485 DEFINE_CHANNEL_OPTION(offchannel
);
8486 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8488 /* Alias set topic to set defaulttopic for compatibility. */
8489 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8492 DEFINE_USER_OPTION(autoinvite
);
8493 DEFINE_USER_OPTION(info
);
8494 DEFINE_USER_OPTION(autoop
);
8496 /* Alias uset autovoice to uset autoop. */
8497 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8499 note_types
= dict_new();
8500 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8503 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8504 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8505 service_register(chanserv
)->trigger
= '!';
8506 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8509 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8511 if(chanserv_conf
.channel_expire_frequency
)
8512 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8514 if(chanserv_conf
.ban_timeout_frequency
)
8515 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8517 if(chanserv_conf
.refresh_period
)
8519 time_t next_refresh
;
8520 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8521 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8524 reg_exit_func(chanserv_db_cleanup
);
8525 message_register_table(msgtab
);