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" },
365 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
366 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
367 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
369 /* Channel note list */
370 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
371 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
372 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
373 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
374 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
375 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
376 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
377 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
378 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
379 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
380 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
381 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
382 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
383 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
384 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
386 /* Channel [un]suspension */
387 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
388 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
389 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
390 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
391 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
392 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
393 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
395 /* Access information */
396 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
397 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
398 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
399 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
400 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
401 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
402 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
403 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
404 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
405 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
406 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
408 /* Seen information */
409 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
410 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
411 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
412 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
414 /* Names information */
415 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
416 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
418 /* Channel information */
419 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
420 { "CSMSG_BAR", "----------------------------------------"},
421 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
422 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
423 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
424 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
425 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
426 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
427 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
428 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
429 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
430 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
431 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
432 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
433 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
434 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
435 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
436 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
437 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
438 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
439 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
440 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
441 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
442 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
443 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
444 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
445 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
446 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
448 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
449 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
450 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
451 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
452 { "CSMSG_PEEK_OPS", "$bOps:$b" },
453 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
454 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
456 /* Network information */
457 { "CSMSG_NETWORK_INFO", "Network Information:" },
458 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
459 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
460 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
461 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
462 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
463 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
464 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
465 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
468 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
469 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
470 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
472 /* Channel searches */
473 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
474 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
475 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
476 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
478 /* Channel configuration */
479 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
480 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
481 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
482 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
485 { "CSMSG_USER_OPTIONS", "User Options:" },
486 // { "CSMSG_USER_PROTECTED", "That user is protected." },
489 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
490 { "CSMSG_PING_RESPONSE", "Pong!" },
491 { "CSMSG_WUT_RESPONSE", "wut" },
492 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
493 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
494 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
495 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
496 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
497 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
498 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
501 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
502 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
506 /* eject_user and unban_user flags */
507 #define ACTION_KICK 0x0001
508 #define ACTION_BAN 0x0002
509 #define ACTION_ADD_LAMER 0x0004
510 #define ACTION_ADD_TIMED_LAMER 0x0008
511 #define ACTION_UNBAN 0x0010
512 #define ACTION_DEL_LAMER 0x0020
514 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
515 #define MODELEN 40 + KEYLEN
519 #define CSFUNC_ARGS user, channel, argc, argv, cmd
521 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
522 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
523 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
524 reply("MSG_MISSING_PARAMS", argv[0]); \
528 DECLARE_LIST(dnrList
, struct do_not_register
*);
529 DEFINE_LIST(dnrList
, struct do_not_register
*);
531 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
533 struct userNode
*chanserv
;
536 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
537 static struct log_type
*CS_LOG
;
538 struct adduserPending
* adduser_pendings
= NULL
;
539 unsigned int adduser_pendings_count
= 0;
543 struct channelList support_channels
;
544 struct mod_chanmode default_modes
;
546 unsigned long db_backup_frequency
;
547 unsigned long channel_expire_frequency
;
548 unsigned long ban_timeout_frequency
;
551 unsigned int adjust_delay
;
552 long channel_expire_delay
;
553 unsigned int nodelete_level
;
555 unsigned int adjust_threshold
;
556 int join_flood_threshold
;
558 unsigned int greeting_length
;
559 unsigned int refresh_period
;
560 unsigned int giveownership_period
;
562 unsigned int max_owned
;
563 unsigned int max_chan_users
;
564 unsigned int max_chan_bans
; /* lamers */
565 unsigned int max_userinfo_length
;
567 struct string_list
*set_shows
;
568 struct string_list
*eightball
;
569 struct string_list
*old_ban_names
;
571 const char *ctcp_short_ban_duration
;
572 const char *ctcp_long_ban_duration
;
574 const char *irc_operator_epithet
;
575 const char *network_helper_epithet
;
576 const char *support_helper_epithet
;
581 struct userNode
*user
;
582 struct userNode
*bot
;
583 struct chanNode
*channel
;
585 unsigned short lowest
;
586 unsigned short highest
;
587 struct userData
**users
;
588 struct helpfile_table table
;
591 enum note_access_type
593 NOTE_SET_CHANNEL_ACCESS
,
594 NOTE_SET_CHANNEL_SETTER
,
598 enum note_visible_type
601 NOTE_VIS_CHANNEL_USERS
,
607 enum note_access_type set_access_type
;
609 unsigned int min_opserv
;
610 unsigned short min_ulevel
;
612 enum note_visible_type visible_type
;
613 unsigned int max_length
;
620 struct note_type
*type
;
621 char setter
[NICKSERV_HANDLE_LEN
+1];
625 static unsigned int registered_channels
;
626 static unsigned int banCount
;
628 static const struct {
631 unsigned short level
;
633 } accessLevels
[] = { /* MUST be orderd less to most! */
634 { "peon", "Peon", UL_PEON
, '+' },
635 { "halfop", "HalfOp", UL_HALFOP
, '%' },
636 { "op", "Op", UL_OP
, '@' },
637 { "manager", "Manager", UL_MANAGER
, '%' },
638 { "coowner", "Coowner", UL_COOWNER
, '*' },
639 { "owner", "Owner", UL_OWNER
, '!' },
640 { "helper", "BUG:", UL_HELPER
, 'X' }
643 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
644 static const struct {
647 unsigned short default_value
;
648 unsigned int old_idx
;
649 unsigned int old_flag
;
650 unsigned short flag_value
;
652 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
653 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
654 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
655 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
656 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
657 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
658 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
659 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
660 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
663 struct charOptionValues
{
666 } automodeValues
[] = {
667 { 'n', "CSMSG_AUTOMODE_NONE" },
668 { 'y', "CSMSG_AUTOMODE_NORMAL" },
669 { 'v', "CSMSG_AUTOMODE_VOICE" },
670 { 'h', "CSMSG_AUTOMODE_HOP" },
671 { 'o', "CSMSG_AUTOMODE_OP" },
672 { 'm', "CSMSG_AUTOMODE_MUTE" }
673 }, protectValues
[] = {
674 { 'a', "CSMSG_PROTECT_ALL" },
675 { 'e', "CSMSG_PROTECT_EQUAL" },
676 { 'l', "CSMSG_PROTECT_LOWER" },
677 { 'n', "CSMSG_PROTECT_NONE" }
679 { 'd', "CSMSG_TOYS_DISABLED" },
680 { 'n', "CSMSG_TOYS_PRIVATE" },
681 { 'p', "CSMSG_TOYS_PUBLIC" }
682 }, topicRefreshValues
[] = {
683 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
684 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
685 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
686 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
687 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
688 }, ctcpReactionValues
[] = {
689 { 'n', "CSMSG_CTCPREACTION_NONE" },
690 { 'k', "CSMSG_CTCPREACTION_KICK" },
691 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
692 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
693 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
694 }, banTimeoutValues
[] = {
695 { '0', "CSMSG_BANTIMEOUT_NONE" },
696 { '1', "CSMSG_BANTIMEOUT_10M" },
697 { '2', "CSMSG_BANTIMEOUT_2H" },
698 { '3', "CSMSG_BANTIMEOUT_4H" },
699 { '4', "CSMSG_BANTIMEOUT_1D" },
700 { '5', "CSMSG_BANTIMEOUT_1W" }
703 static const struct {
707 unsigned int old_idx
;
709 struct charOptionValues
*values
;
711 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues
), automodeValues
},
712 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
713 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
714 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
715 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
},
716 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues
), banTimeoutValues
}
719 struct userData
*helperList
;
720 struct chanData
*channelList
;
721 static struct module *chanserv_module
;
722 static unsigned int userCount
;
723 unsigned int chanserv_read_version
= 0; /* db version control */
725 #define CHANSERV_DB_VERSION 2
727 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
728 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
729 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
732 user_level_from_name(const char *name
, unsigned short clamp_level
)
734 unsigned int level
= 0, ii
;
736 level
= strtoul(name
, NULL
, 10);
737 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
738 if(!irccasecmp(name
, accessLevels
[ii
].name
))
739 level
= accessLevels
[ii
].level
;
740 if(level
> clamp_level
)
746 user_level_name_from_level(int level
)
754 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
755 if(level
>= accessLevels
[ii
].level
)
756 highest
= accessLevels
[ii
].title
;
762 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
765 *minl
= strtoul(arg
, &sep
, 10);
773 *maxl
= strtoul(sep
+1, &sep
, 10);
781 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
783 struct userData
*uData
, **head
;
785 if(!channel
|| !handle
)
788 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
789 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
791 for(uData
= helperList
;
792 uData
&& uData
->handle
!= handle
;
793 uData
= uData
->next
);
797 uData
= calloc(1, sizeof(struct userData
));
798 uData
->handle
= handle
;
800 uData
->access
= UL_HELPER
;
806 uData
->next
= helperList
;
808 helperList
->prev
= uData
;
816 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
817 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
820 head
= &(channel
->users
);
823 if(uData
&& (uData
!= *head
))
825 /* Shuffle the user to the head of whatever list he was in. */
827 uData
->next
->prev
= uData
->prev
;
829 uData
->prev
->next
= uData
->next
;
835 (**head
).prev
= uData
;
842 /* Returns non-zero if user has at least the minimum access.
843 * exempt_owner is set when handling !set, so the owner can set things
846 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
848 struct userData
*uData
;
849 struct chanData
*cData
= channel
->channel_info
;
850 unsigned short minimum
= cData
->lvlOpts
[opt
];
853 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
856 if(minimum
<= uData
->access
)
858 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
863 /* Scan for other users authenticated to the same handle
864 still in the channel. If so, keep them listed as present.
866 user is optional, if not null, it skips checking that userNode
867 (for the handle_part function) */
869 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
873 if(IsSuspended(uData
->channel
)
874 || IsUserSuspended(uData
)
875 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
887 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
889 unsigned int eflags
, argc
;
891 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
893 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
894 if(!channel
->channel_info
895 || IsSuspended(channel
->channel_info
)
897 || !ircncasecmp(text
, "ACTION ", 7))
899 /* We dont punish people we know -Rubin
900 * * Figure out the minimum level needed to CTCP the channel *
902 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
905 /* If they are a user of the channel, they are exempt */
906 if(_GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
908 /* We need to enforce against them; do so. */
911 argv
[1] = user
->nick
;
913 if(GetUserMode(channel
, user
))
914 eflags
|= ACTION_KICK
;
915 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
916 default: case 'n': return;
918 eflags
|= ACTION_KICK
;
921 eflags
|= ACTION_BAN
;
924 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
925 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
928 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
929 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
932 argv
[argc
++] = bad_ctcp_reason
;
933 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
937 chanserv_create_note_type(const char *name
)
939 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
940 strcpy(ntype
->name
, name
);
942 dict_insert(note_types
, ntype
->name
, ntype
);
947 chanserv_deref_note_type(void *data
)
949 struct note_type
*ntype
= data
;
951 if(--ntype
->refs
> 0)
957 chanserv_flush_note_type(struct note_type
*ntype
)
959 struct chanData
*cData
;
960 for(cData
= channelList
; cData
; cData
= cData
->next
)
961 dict_remove(cData
->notes
, ntype
->name
);
965 chanserv_truncate_notes(struct note_type
*ntype
)
967 struct chanData
*cData
;
969 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
971 for(cData
= channelList
; cData
; cData
= cData
->next
) {
972 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
975 if(strlen(note
->note
) <= ntype
->max_length
)
977 dict_remove2(cData
->notes
, ntype
->name
, 1);
978 note
= realloc(note
, size
);
979 note
->note
[ntype
->max_length
] = 0;
980 dict_insert(cData
->notes
, ntype
->name
, note
);
984 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
987 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
990 unsigned int len
= strlen(text
);
992 if(len
> type
->max_length
) len
= type
->max_length
;
993 note
= calloc(1, sizeof(*note
) + len
);
995 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
996 memcpy(note
->note
, text
, len
);
998 dict_insert(channel
->notes
, type
->name
, note
);
1004 chanserv_free_note(void *data
)
1006 struct note
*note
= data
;
1008 chanserv_deref_note_type(note
->type
);
1009 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
1013 static MODCMD_FUNC(cmd_createnote
) {
1014 struct note_type
*ntype
;
1015 unsigned int arg
= 1, existed
= 0, max_length
;
1017 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
1020 ntype
= chanserv_create_note_type(argv
[arg
]);
1021 if(!irccasecmp(argv
[++arg
], "privileged"))
1024 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
1025 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
1027 else if(!irccasecmp(argv
[arg
], "channel"))
1029 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
1032 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
1035 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
1036 ntype
->set_access
.min_ulevel
= ulvl
;
1038 else if(!irccasecmp(argv
[arg
], "setter"))
1040 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1044 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1048 if(!irccasecmp(argv
[++arg
], "privileged"))
1049 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1050 else if(!irccasecmp(argv
[arg
], "channel_users"))
1051 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1052 else if(!irccasecmp(argv
[arg
], "all"))
1053 ntype
->visible_type
= NOTE_VIS_ALL
;
1055 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1059 if((arg
+1) >= argc
) {
1060 reply("MSG_MISSING_PARAMS", argv
[0]);
1063 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1064 if(max_length
< 20 || max_length
> 450)
1066 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1069 if(existed
&& (max_length
< ntype
->max_length
))
1071 ntype
->max_length
= max_length
;
1072 chanserv_truncate_notes(ntype
);
1074 ntype
->max_length
= max_length
;
1077 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1079 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1084 dict_remove(note_types
, ntype
->name
);
1088 static MODCMD_FUNC(cmd_removenote
) {
1089 struct note_type
*ntype
;
1092 ntype
= dict_find(note_types
, argv
[1], NULL
);
1093 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1096 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1103 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1106 chanserv_flush_note_type(ntype
);
1108 dict_remove(note_types
, argv
[1]);
1109 reply("CSMSG_NOTE_DELETED", argv
[1]);
1114 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1118 if(orig
->modes_set
& change
->modes_clear
)
1120 if(orig
->modes_clear
& change
->modes_set
)
1122 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1123 && strcmp(orig
->new_key
, change
->new_key
))
1125 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1126 && (orig
->new_limit
!= change
->new_limit
))
1131 static char max_length_text
[MAXLEN
+1][16];
1133 static struct helpfile_expansion
1134 chanserv_expand_variable(const char *variable
)
1136 struct helpfile_expansion exp
;
1138 if(!irccasecmp(variable
, "notes"))
1141 exp
.type
= HF_TABLE
;
1142 exp
.value
.table
.length
= 1;
1143 exp
.value
.table
.width
= 3;
1144 exp
.value
.table
.flags
= 0;
1145 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1146 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1147 exp
.value
.table
.contents
[0][0] = "Note Type";
1148 exp
.value
.table
.contents
[0][1] = "Visibility";
1149 exp
.value
.table
.contents
[0][2] = "Max Length";
1150 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1152 struct note_type
*ntype
= iter_data(it
);
1155 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1156 row
= exp
.value
.table
.length
++;
1157 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1158 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1159 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1160 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1162 if(!max_length_text
[ntype
->max_length
][0])
1163 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1164 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1169 exp
.type
= HF_STRING
;
1170 exp
.value
.str
= NULL
;
1174 static struct chanData
*
1175 register_channel(struct chanNode
*cNode
, char *registrar
)
1177 struct chanData
*channel
;
1178 enum levelOption lvlOpt
;
1179 enum charOption chOpt
;
1181 channel
= calloc(1, sizeof(struct chanData
));
1183 channel
->notes
= dict_new();
1184 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1186 channel
->registrar
= strdup(registrar
);
1187 channel
->registered
= now
;
1188 channel
->visited
= now
;
1189 channel
->limitAdjusted
= now
;
1190 channel
->ownerTransfer
= now
;
1191 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1192 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1193 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1194 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1195 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1197 channel
->prev
= NULL
;
1198 channel
->next
= channelList
;
1201 channelList
->prev
= channel
;
1202 channelList
= channel
;
1203 registered_channels
++;
1205 channel
->channel
= cNode
;
1207 cNode
->channel_info
= channel
;
1212 static struct userData
*
1213 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1215 struct userData
*ud
;
1217 if(access
> UL_OWNER
)
1220 ud
= calloc(1, sizeof(*ud
));
1221 ud
->channel
= channel
;
1222 ud
->handle
= handle
;
1224 ud
->access
= access
;
1225 ud
->info
= info
? strdup(info
) : NULL
;
1228 ud
->next
= channel
->users
;
1230 channel
->users
->prev
= ud
;
1231 channel
->users
= ud
;
1233 channel
->userCount
++;
1237 ud
->u_next
= ud
->handle
->channels
;
1239 ud
->u_next
->u_prev
= ud
;
1240 ud
->handle
->channels
= ud
;
1242 ud
->flags
= USER_FLAGS_DEFAULT
;
1246 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1249 del_channel_user(struct userData
*user
, int do_gc
)
1251 struct chanData
*channel
= user
->channel
;
1253 channel
->userCount
--;
1257 user
->prev
->next
= user
->next
;
1259 channel
->users
= user
->next
;
1261 user
->next
->prev
= user
->prev
;
1264 user
->u_prev
->u_next
= user
->u_next
;
1266 user
->handle
->channels
= user
->u_next
;
1268 user
->u_next
->u_prev
= user
->u_prev
;
1272 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1273 unregister_channel(channel
, "lost all users.");
1276 static struct adduserPending
*
1277 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1279 struct adduserPending
*ap
;
1280 ap
= calloc(1,sizeof(struct adduserPending
));
1281 ap
->channel
= channel
;
1284 ap
->created
= time(NULL
);
1286 /* ap->prev defaults to NULL already.. */
1287 ap
->next
= adduser_pendings
;
1288 if(adduser_pendings
)
1289 adduser_pendings
->prev
= ap
;
1290 adduser_pendings
= ap
;
1291 adduser_pendings_count
++;
1296 del_adduser_pending(struct adduserPending
*ap
)
1299 ap
->prev
->next
= ap
->next
;
1301 adduser_pendings
= ap
->next
;
1304 ap
->next
->prev
= ap
->prev
;
1308 static void expire_adduser_pending();
1310 /* find_adduser_pending(channel, user) will find an arbitrary record
1311 * from user, channel, or user and channel.
1312 * if user or channel are NULL, they will match any records.
1314 static struct adduserPending
*
1315 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1317 struct adduserPending
*ap
;
1319 expire_adduser_pending(); /* why not here.. */
1321 if(!channel
&& !user
) /* 2 nulls matches all */
1322 return(adduser_pendings
);
1323 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1325 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1332 /* Remove all pendings for a user or channel
1334 * called in nickserv.c DelUser() and proto-* unregister_channel()
1337 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1339 struct adduserPending
*ap
;
1341 /* So this is a bit wastefull, i hate dealing with linked lists.
1342 * if its a problem we'll rewrite it right */
1343 while((ap
= find_adduser_pending(channel
, user
))) {
1344 del_adduser_pending(ap
);
1348 /* Called from nickserv.c cmd_auth after someone auths */
1350 process_adduser_pending(struct userNode
*user
)
1352 struct adduserPending
*ap
;
1353 if(!user
->handle_info
)
1354 return; /* not associated with an account */
1355 while((ap
= find_adduser_pending(NULL
, user
)))
1357 struct userData
*actee
;
1358 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1360 /* Already on the userlist. do nothing*/
1364 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1365 scan_user_presence(actee
, NULL
);
1367 del_adduser_pending(ap
);
1372 expire_adduser_pending()
1374 struct adduserPending
*ap
, *ap_next
;
1375 ap
= adduser_pendings
;
1378 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1380 ap_next
= ap
->next
; /* save next */
1381 del_adduser_pending(ap
); /* free and relink */
1382 ap
= ap_next
; /* advance */
1389 static void expire_ban(void *data
);
1391 static struct banData
*
1392 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1395 unsigned int ii
, l1
, l2
;
1400 bd
= malloc(sizeof(struct banData
));
1402 bd
->channel
= channel
;
1404 bd
->triggered
= triggered
;
1405 bd
->expires
= expires
;
1407 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1409 extern const char *hidden_host_suffix
;
1410 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1414 l2
= strlen(old_name
);
1417 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1419 new_mask
= alloca(MAXLEN
);
1420 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1423 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1425 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1426 bd
->reason
= strdup(reason
);
1429 timeq_add(expires
, expire_ban
, bd
);
1432 bd
->next
= channel
->bans
; /* lamers */
1434 channel
->bans
->prev
= bd
;
1436 channel
->banCount
++;
1443 del_channel_ban(struct banData
*ban
)
1445 ban
->channel
->banCount
--;
1449 ban
->prev
->next
= ban
->next
;
1451 ban
->channel
->bans
= ban
->next
;
1454 ban
->next
->prev
= ban
->prev
;
1457 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1466 expire_ban(void *data
) /* lamer.. */
1468 struct banData
*bd
= data
;
1469 if(!IsSuspended(bd
->channel
))
1471 struct banList bans
;
1472 struct mod_chanmode change
;
1474 bans
= bd
->channel
->channel
->banlist
;
1475 mod_chanmode_init(&change
);
1476 for(ii
=0; ii
<bans
.used
; ii
++)
1478 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1481 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1482 change
.args
[0].u
.hostmask
= bd
->mask
;
1483 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1489 del_channel_ban(bd
);
1492 static void chanserv_expire_suspension(void *data
);
1495 unregister_channel(struct chanData
*channel
, const char *reason
)
1497 struct mod_chanmode change
;
1498 char msgbuf
[MAXLEN
];
1500 /* After channel unregistration, the following must be cleaned
1502 - Channel information.
1504 - Channel bans. (lamers)
1505 - Channel suspension data.
1506 - adduser_pending data.
1507 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1513 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1517 mod_chanmode_init(&change
);
1518 change
.modes_clear
|= MODE_REGISTERED
;
1519 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1522 wipe_adduser_pending(channel
->channel
, NULL
);
1524 while(channel
->users
)
1525 del_channel_user(channel
->users
, 0);
1527 while(channel
->bans
)
1528 del_channel_ban(channel
->bans
);
1530 free(channel
->topic
);
1531 free(channel
->registrar
);
1532 free(channel
->greeting
);
1533 free(channel
->user_greeting
);
1534 free(channel
->topic_mask
);
1537 channel
->prev
->next
= channel
->next
;
1539 channelList
= channel
->next
;
1542 channel
->next
->prev
= channel
->prev
;
1544 if(channel
->suspended
)
1546 struct chanNode
*cNode
= channel
->channel
;
1547 struct suspended
*suspended
, *next_suspended
;
1549 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1551 next_suspended
= suspended
->previous
;
1552 free(suspended
->suspender
);
1553 free(suspended
->reason
);
1554 if(suspended
->expires
)
1555 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1560 cNode
->channel_info
= NULL
;
1562 channel
->channel
->channel_info
= NULL
;
1564 dict_delete(channel
->notes
);
1565 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1566 if(!IsSuspended(channel
))
1567 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1568 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1569 UnlockChannel(channel
->channel
);
1571 registered_channels
--;
1575 expire_channels(UNUSED_ARG(void *data
))
1577 struct chanData
*channel
, *next
;
1578 struct userData
*user
;
1579 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1581 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1582 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1584 for(channel
= channelList
; channel
; channel
= next
)
1586 next
= channel
->next
;
1588 /* See if the channel can be expired. */
1589 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1590 || IsProtected(channel
))
1593 /* Make sure there are no high-ranking users still in the channel. */
1594 for(user
=channel
->users
; user
; user
=user
->next
)
1595 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1600 /* Unregister the channel */
1601 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1602 unregister_channel(channel
, "registration expired.");
1605 if(chanserv_conf
.channel_expire_frequency
)
1606 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1610 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1612 char protect
= channel
->chOpts
[chProtect
];
1613 struct userData
*cs_victim
, *cs_aggressor
;
1615 /* Don't protect if no one is to be protected, someone is attacking
1616 himself, or if the aggressor is an IRC Operator. */
1617 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1620 /* Don't protect if the victim isn't authenticated (because they
1621 can't be a channel user), unless we are to protect non-users
1623 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1624 if(protect
!= 'a' && !cs_victim
)
1627 /* Protect if the aggressor isn't a user because at this point,
1628 the aggressor can only be less than or equal to the victim. */
1629 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1633 /* If the aggressor was a user, then the victim can't be helped. */
1640 if(cs_victim
->access
> cs_aggressor
->access
)
1645 if(cs_victim
->access
>= cs_aggressor
->access
)
1654 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1656 struct chanData
*cData
= channel
->channel_info
;
1657 struct userData
*cs_victim
;
1659 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1660 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1661 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1663 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1671 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1673 struct chanData
*cData
= channel
->channel_info
;
1674 struct userData
*cs_victim
;
1676 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1677 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1678 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1680 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1689 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1691 if(IsService(victim
))
1693 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1697 if(protect_user(victim
, user
, channel
->channel_info
))
1699 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1707 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1709 if(IsService(victim
))
1711 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1715 if(protect_user(victim
, user
, channel
->channel_info
))
1717 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1724 static struct do_not_register
*
1725 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1727 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1728 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1729 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1730 strcpy(dnr
->reason
, reason
);
1732 if(dnr
->chan_name
[0] == '*')
1733 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1734 else if(strpbrk(dnr
->chan_name
, "*?"))
1735 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1737 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1741 static struct dnrList
1742 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1744 struct dnrList list
;
1746 struct do_not_register
*dnr
;
1748 dnrList_init(&list
);
1749 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1750 dnrList_append(&list
, dnr
);
1751 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1752 dnrList_append(&list
, dnr
);
1754 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1755 if(match_ircglob(chan_name
, iter_key(it
)))
1756 dnrList_append(&list
, iter_data(it
));
1761 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1763 struct dnrList list
;
1764 struct do_not_register
*dnr
;
1766 char buf
[INTERVALLEN
];
1768 list
= chanserv_find_dnrs(chan_name
, handle
);
1769 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1771 dnr
= list
.list
[ii
];
1774 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1775 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1778 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1781 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1786 struct do_not_register
*
1787 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1789 struct do_not_register
*dnr
;
1792 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1796 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1798 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1799 if(match_ircglob(chan_name
, iter_key(it
)))
1800 return iter_data(it
);
1805 static CHANSERV_FUNC(cmd_noregister
)
1808 struct do_not_register
*dnr
;
1809 char buf
[INTERVALLEN
];
1810 unsigned int matches
;
1816 reply("CSMSG_DNR_SEARCH_RESULTS");
1817 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1820 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1822 dnr
= iter_data(it
);
1824 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1826 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1829 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1831 dnr
= iter_data(it
);
1833 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1835 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1838 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1840 dnr
= iter_data(it
);
1842 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1844 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1849 reply("MSG_MATCH_COUNT", matches
);
1851 reply("MSG_NO_MATCHES");
1857 if(!IsChannelName(target
) && (*target
!= '*'))
1859 reply("CSMSG_NOT_DNR", target
);
1865 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1866 if((*target
== '*') && !get_handle_info(target
+ 1))
1868 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1871 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1872 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1876 reply("CSMSG_DNR_SEARCH_RESULTS");
1877 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
1880 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1882 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1884 reply("MSG_NO_MATCHES");
1888 static CHANSERV_FUNC(cmd_allowregister
)
1890 const char *chan_name
= argv
[1];
1892 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1894 dict_remove(handle_dnrs
, chan_name
+1);
1895 reply("CSMSG_DNR_REMOVED", chan_name
);
1897 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1899 dict_remove(plain_dnrs
, chan_name
);
1900 reply("CSMSG_DNR_REMOVED", chan_name
);
1902 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1904 dict_remove(mask_dnrs
, chan_name
);
1905 reply("CSMSG_DNR_REMOVED", chan_name
);
1909 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1916 chanserv_get_owned_count(struct handle_info
*hi
)
1918 struct userData
*cList
;
1921 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1922 if(cList
->access
== UL_OWNER
)
1927 static CHANSERV_FUNC(cmd_register
)
1929 struct handle_info
*handle
;
1930 struct chanData
*cData
;
1931 struct modeNode
*mn
;
1932 char reason
[MAXLEN
];
1934 unsigned int new_channel
, force
=0;
1935 struct do_not_register
*dnr
;
1941 if(channel
->channel_info
)
1943 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1947 if(channel
->bad_channel
)
1949 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1953 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1955 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1960 chan_name
= channel
->name
;
1966 reply("MSG_MISSING_PARAMS", cmd
->name
);
1967 svccmd_send_help_brief(user
, chanserv
, cmd
);
1970 if(!IsChannelName(argv
[1]))
1972 reply("MSG_NOT_CHANNEL_NAME");
1976 if(opserv_bad_channel(argv
[1]))
1978 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1983 chan_name
= argv
[1];
1986 if(argc
>= (new_channel
+2))
1988 if(!IsHelping(user
))
1990 reply("CSMSG_PROXY_FORBIDDEN");
1994 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1996 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1997 dnr
= chanserv_is_dnr(chan_name
, handle
);
1999 /* Check if they are over the limit.. */
2000 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2002 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
2009 handle
= user
->handle_info
;
2010 dnr
= chanserv_is_dnr(chan_name
, handle
);
2011 /* Check if they are over the limit.. */
2012 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
2014 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
2017 /* Check if another service is in the channel */
2019 for(n
= 0; n
< channel
->members
.used
; n
++)
2021 mn
= channel
->members
.list
[n
];
2022 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
2024 reply("CSMSG_ANOTHER_SERVICE");
2031 if(!IsHelping(user
))
2032 reply("CSMSG_DNR_CHANNEL", chan_name
);
2034 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
2038 /* now handled above for message specilization *
2039 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2041 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2047 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2049 cData
= register_channel(channel
, user
->handle_info
->handle
);
2050 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2051 cData
->modes
= chanserv_conf
.default_modes
;
2053 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2054 if (IsOffChannel(cData
))
2056 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2060 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2061 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2062 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2064 mod_chanmode_announce(chanserv
, channel
, change
);
2065 mod_chanmode_free(change
);
2068 /* Initialize the channel's max user record. */
2069 cData
->max
= channel
->members
.used
;
2071 if(handle
!= user
->handle_info
)
2072 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2074 reply("CSMSG_REG_SUCCESS", channel
->name
);
2076 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2077 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2082 make_confirmation_string(struct userData
*uData
)
2084 static char strbuf
[16];
2089 for(src
= uData
->handle
->handle
; *src
; )
2090 accum
= accum
* 31 + toupper(*src
++);
2092 for(src
= uData
->channel
->channel
->name
; *src
; )
2093 accum
= accum
* 31 + toupper(*src
++);
2094 sprintf(strbuf
, "%08x", accum
);
2098 static CHANSERV_FUNC(cmd_unregister
)
2101 char reason
[MAXLEN
];
2102 struct chanData
*cData
;
2103 struct userData
*uData
;
2105 cData
= channel
->channel_info
;
2108 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2112 uData
= GetChannelUser(cData
, user
->handle_info
);
2113 if(!uData
|| (uData
->access
< UL_OWNER
))
2115 reply("CSMSG_NO_ACCESS");
2119 if(IsProtected(cData
))
2121 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2125 if(!IsHelping(user
))
2127 const char *confirm_string
;
2128 if(IsSuspended(cData
))
2130 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2133 confirm_string
= make_confirmation_string(uData
);
2134 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2136 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2141 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2142 name
= strdup(channel
->name
);
2143 unregister_channel(cData
, reason
);
2144 reply("CSMSG_UNREG_SUCCESS", name
);
2149 static CHANSERV_FUNC(cmd_move
)
2151 struct mod_chanmode change
;
2152 struct chanNode
*target
;
2153 struct modeNode
*mn
;
2154 struct userData
*uData
;
2155 char reason
[MAXLEN
];
2156 struct do_not_register
*dnr
;
2160 if(IsProtected(channel
->channel_info
))
2162 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2166 if(!IsChannelName(argv
[1]))
2168 reply("MSG_NOT_CHANNEL_NAME");
2172 if(opserv_bad_channel(argv
[1]))
2174 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2178 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2180 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2182 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2184 if(!IsHelping(user
))
2185 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2187 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2193 mod_chanmode_init(&change
);
2194 if(!(target
= GetChannel(argv
[1])))
2196 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2197 if(!IsSuspended(channel
->channel_info
))
2198 AddChannelUser(chanserv
, target
);
2200 else if(target
->channel_info
)
2202 reply("CSMSG_ALREADY_REGGED", target
->name
);
2205 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2206 && !IsHelping(user
))
2208 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2211 else if(!IsSuspended(channel
->channel_info
))
2214 change
.args
[0].mode
= MODE_CHANOP
;
2215 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2216 mod_chanmode_announce(chanserv
, target
, &change
);
2221 /* Clear MODE_REGISTERED from old channel, add it to new. */
2223 change
.modes_clear
= MODE_REGISTERED
;
2224 mod_chanmode_announce(chanserv
, channel
, &change
);
2225 change
.modes_clear
= 0;
2226 change
.modes_set
= MODE_REGISTERED
;
2227 mod_chanmode_announce(chanserv
, target
, &change
);
2230 /* Move the channel_info to the target channel; it
2231 shouldn't be necessary to clear timeq callbacks
2232 for the old channel. */
2233 target
->channel_info
= channel
->channel_info
;
2234 target
->channel_info
->channel
= target
;
2235 channel
->channel_info
= NULL
;
2237 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2239 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2240 if(!IsSuspended(target
->channel_info
))
2242 char reason2
[MAXLEN
];
2243 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2244 DelChannelUser(chanserv
, channel
, reason2
, 0);
2246 UnlockChannel(channel
);
2247 LockChannel(target
);
2248 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2253 merge_users(struct chanData
*source
, struct chanData
*target
)
2255 struct userData
*suData
, *tuData
, *next
;
2261 /* Insert the source's users into the scratch area. */
2262 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2263 dict_insert(merge
, suData
->handle
->handle
, suData
);
2265 /* Iterate through the target's users, looking for
2266 users common to both channels. The lower access is
2267 removed from either the scratch area or target user
2269 for(tuData
= target
->users
; tuData
; tuData
= next
)
2271 struct userData
*choice
;
2273 next
= tuData
->next
;
2275 /* If a source user exists with the same handle as a target
2276 channel's user, resolve the conflict by removing one. */
2277 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2281 /* Pick the data we want to keep. */
2282 /* If the access is the same, use the later seen time. */
2283 if(suData
->access
== tuData
->access
)
2284 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2285 else /* Otherwise, keep the higher access level. */
2286 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2288 /* Remove the user that wasn't picked. */
2289 if(choice
== tuData
)
2291 dict_remove(merge
, suData
->handle
->handle
);
2292 del_channel_user(suData
, 0);
2295 del_channel_user(tuData
, 0);
2298 /* Move the remaining users to the target channel. */
2299 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2301 suData
= iter_data(it
);
2303 /* Insert the user into the target channel's linked list. */
2304 suData
->prev
= NULL
;
2305 suData
->next
= target
->users
;
2306 suData
->channel
= target
;
2309 target
->users
->prev
= suData
;
2310 target
->users
= suData
;
2312 /* Update the user counts for the target channel; the
2313 source counts are left alone. */
2314 target
->userCount
++;
2317 /* Possible to assert (source->users == NULL) here. */
2318 source
->users
= NULL
;
2323 merge_bans(struct chanData
*source
, struct chanData
*target
)
2325 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2327 /* Hold on to the original head of the target ban list
2328 to avoid comparing source bans with source bans. */
2329 tFront
= target
->bans
;
2331 /* Perform a totally expensive O(n*m) merge, ick. */
2332 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2334 /* Flag to track whether the ban's been moved
2335 to the destination yet. */
2338 /* Possible to assert (sbData->prev == NULL) here. */
2339 sNext
= sbData
->next
;
2341 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2343 tNext
= tbData
->next
;
2345 /* Perform two comparisons between each source
2346 and target ban, conflicts are resolved by
2347 keeping the broader ban and copying the later
2348 expiration and triggered time. */
2349 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2351 /* There is a broader ban in the target channel that
2352 overrides one in the source channel; remove the
2353 source ban and break. */
2354 if(sbData
->expires
> tbData
->expires
)
2355 tbData
->expires
= sbData
->expires
;
2356 if(sbData
->triggered
> tbData
->triggered
)
2357 tbData
->triggered
= sbData
->triggered
;
2358 del_channel_ban(sbData
);
2361 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2363 /* There is a broader ban in the source channel that
2364 overrides one in the target channel; remove the
2365 target ban, fall through and move the source over. */
2366 if(tbData
->expires
> sbData
->expires
)
2367 sbData
->expires
= tbData
->expires
;
2368 if(tbData
->triggered
> sbData
->triggered
)
2369 sbData
->triggered
= tbData
->triggered
;
2370 if(tbData
== tFront
)
2372 del_channel_ban(tbData
);
2375 /* Source bans can override multiple target bans, so
2376 we allow a source to run through this loop multiple
2377 times, but we can only move it once. */
2382 /* Remove the source ban from the source ban list. */
2384 sbData
->next
->prev
= sbData
->prev
;
2386 /* Modify the source ban's associated channel. */
2387 sbData
->channel
= target
;
2389 /* Insert the ban into the target channel's linked list. */
2390 sbData
->prev
= NULL
;
2391 sbData
->next
= target
->bans
;
2394 target
->bans
->prev
= sbData
;
2395 target
->bans
= sbData
;
2397 /* Update the user counts for the target channel. */
2402 /* Possible to assert (source->bans == NULL) here. */
2403 source
->bans
= NULL
;
2407 merge_data(struct chanData
*source
, struct chanData
*target
)
2409 /* Use more recent visited and owner-transfer time; use older
2410 * registered time. Bitwise or may_opchan. Use higher max.
2411 * Do not touch last_refresh, ban count or user counts.
2413 if(source
->visited
> target
->visited
)
2414 target
->visited
= source
->visited
;
2415 if(source
->registered
< target
->registered
)
2416 target
->registered
= source
->registered
;
2417 if(source
->ownerTransfer
> target
->ownerTransfer
)
2418 target
->ownerTransfer
= source
->ownerTransfer
;
2419 if(source
->may_opchan
)
2420 target
->may_opchan
= 1;
2421 if(source
->max
> target
->max
)
2422 target
->max
= source
->max
;
2426 merge_channel(struct chanData
*source
, struct chanData
*target
)
2428 merge_users(source
, target
);
2429 merge_bans(source
, target
);
2430 merge_data(source
, target
);
2433 static CHANSERV_FUNC(cmd_merge
)
2435 struct userData
*target_user
;
2436 struct chanNode
*target
;
2437 char reason
[MAXLEN
];
2441 /* Make sure the target channel exists and is registered to the user
2442 performing the command. */
2443 if(!(target
= GetChannel(argv
[1])))
2445 reply("MSG_INVALID_CHANNEL");
2449 if(!target
->channel_info
)
2451 reply("CSMSG_NOT_REGISTERED", target
->name
);
2455 if(IsProtected(channel
->channel_info
))
2457 reply("CSMSG_MERGE_NODELETE");
2461 if(IsSuspended(target
->channel_info
))
2463 reply("CSMSG_MERGE_SUSPENDED");
2467 if(channel
== target
)
2469 reply("CSMSG_MERGE_SELF");
2473 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2474 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2476 reply("CSMSG_MERGE_NOT_OWNER");
2480 /* Merge the channel structures and associated data. */
2481 merge_channel(channel
->channel_info
, target
->channel_info
);
2482 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2483 unregister_channel(channel
->channel_info
, reason
);
2484 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2488 static CHANSERV_FUNC(cmd_opchan
)
2490 struct mod_chanmode change
;
2491 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2493 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2496 channel
->channel_info
->may_opchan
= 0;
2497 mod_chanmode_init(&change
);
2499 change
.args
[0].mode
= MODE_CHANOP
;
2500 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2501 mod_chanmode_announce(chanserv
, channel
, &change
);
2502 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2506 static CHANSERV_FUNC(cmd_adduser
)
2508 struct userData
*actee
;
2509 struct userData
*actor
;
2510 struct handle_info
*handle
;
2511 unsigned short access
;
2515 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2517 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2521 access
= user_level_from_name(argv
[2], UL_OWNER
);
2524 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2528 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2529 if(actor
->access
<= access
)
2531 reply("CSMSG_NO_BUMP_ACCESS");
2535 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2537 // 'kevin must first authenticate with AuthServ.' is sent to user
2538 struct userNode
*unode
;
2539 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2542 if(find_adduser_pending(channel
, unode
)) {
2543 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2546 if(IsInChannel(channel
, unode
)) {
2547 reply("CSMSG_ADDUSER_PENDING");
2548 add_adduser_pending(channel
, unode
, access
);
2549 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2551 /* this results in user must auth AND not in chan errors. too confusing..
2553 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2561 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2563 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2567 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2568 scan_user_presence(actee
, NULL
);
2569 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2573 static CHANSERV_FUNC(cmd_clvl
)
2575 struct handle_info
*handle
;
2576 struct userData
*victim
;
2577 struct userData
*actor
;
2578 unsigned short new_access
;
2579 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2583 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2585 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2588 if(handle
== user
->handle_info
&& !privileged
)
2590 reply("CSMSG_NO_SELF_CLVL");
2594 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2596 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2600 if(actor
->access
<= victim
->access
&& !privileged
)
2602 reply("MSG_USER_OUTRANKED", handle
->handle
);
2606 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2610 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2614 if(new_access
>= actor
->access
&& !privileged
)
2616 reply("CSMSG_NO_BUMP_ACCESS");
2620 victim
->access
= new_access
;
2621 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2625 static CHANSERV_FUNC(cmd_deluser
)
2627 struct handle_info
*handle
;
2628 struct userData
*victim
;
2629 struct userData
*actor
;
2630 unsigned short access
;
2635 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2637 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2640 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2642 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2648 access
= user_level_from_name(argv
[1], UL_OWNER
);
2651 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2654 if(access
!= victim
->access
)
2656 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2662 access
= victim
->access
;
2665 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2667 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2671 chan_name
= strdup(channel
->name
);
2672 del_channel_user(victim
, 1);
2673 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2679 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2681 struct userData
*actor
, *uData
, *next
;
2683 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2685 if(min_access
> max_access
)
2687 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2691 if((actor
->access
<= max_access
) && !IsHelping(user
))
2693 reply("CSMSG_NO_ACCESS");
2697 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2701 if((uData
->access
>= min_access
)
2702 && (uData
->access
<= max_access
)
2703 && match_ircglob(uData
->handle
->handle
, mask
))
2704 del_channel_user(uData
, 1);
2707 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2711 static CHANSERV_FUNC(cmd_mdelowner
)
2713 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2716 static CHANSERV_FUNC(cmd_mdelcoowner
)
2718 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2721 static CHANSERV_FUNC(cmd_mdelmanager
)
2723 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2726 static CHANSERV_FUNC(cmd_mdelop
)
2728 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2731 static CHANSERV_FUNC(cmd_mdelpeon
)
2733 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2736 static CHANSERV_FUNC(cmd_mdelhalfop
)
2738 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2744 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2746 struct banData
*bData
, *next
;
2747 char interval
[INTERVALLEN
];
2752 limit
= now
- duration
;
2753 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2757 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2760 del_channel_ban(bData
);
2764 intervalString(interval
, duration
, user
->handle_info
);
2765 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2770 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
, int vacation
)
2772 struct userData
*actor
, *uData
, *next
;
2773 char interval
[INTERVALLEN
];
2777 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2778 if(min_access
> max_access
)
2780 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2784 if((actor
->access
<= max_access
) && !IsHelping(user
))
2786 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2791 limit
= now
- duration
;
2792 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2796 if((uData
->seen
> limit
)
2798 || (HANDLE_FLAGGED(uData
->handle
, FROZEN
) && !vacation
))
2801 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2802 || (!max_access
&& (uData
->access
< actor
->access
)))
2804 del_channel_user(uData
, 1);
2812 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2814 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2818 static CHANSERV_FUNC(cmd_trim
)
2820 unsigned long duration
;
2821 unsigned short min_level
, max_level
;
2826 vacation
= argc
> 3 && !strcmp(argv
[3], "vacation");
2827 duration
= ParseInterval(argv
[2]);
2830 reply("CSMSG_CANNOT_TRIM");
2834 if(!irccasecmp(argv
[1], "lamers"))
2836 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2839 else if(!irccasecmp(argv
[1], "users"))
2841 cmd_trim_users(user
, channel
, 0, 0, duration
, vacation
);
2844 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2846 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
, vacation
);
2849 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2851 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
, vacation
);
2856 reply("CSMSG_INVALID_TRIM", argv
[1]);
2861 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2862 to the user. cmd_all takes advantage of this. */
2863 static CHANSERV_FUNC(cmd_up
)
2865 struct mod_chanmode change
;
2866 struct userData
*uData
;
2869 mod_chanmode_init(&change
);
2871 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2872 if(!change
.args
[0].u
.member
)
2875 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2879 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2883 reply("CSMSG_GODMODE_UP", argv
[0]);
2886 else if(uData
->access
>= UL_OP
)
2888 change
.args
[0].mode
= MODE_CHANOP
;
2889 errmsg
= "CSMSG_ALREADY_OPPED";
2891 else if(uData
->access
>= UL_HALFOP
)
2893 change
.args
[0].mode
= MODE_HALFOP
;
2894 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2896 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chAutomode
] != 'm' ))
2898 change
.args
[0].mode
= MODE_VOICE
;
2899 errmsg
= "CSMSG_ALREADY_VOICED";
2904 reply("CSMSG_NO_ACCESS");
2907 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2908 if(!change
.args
[0].mode
)
2911 reply(errmsg
, channel
->name
);
2914 modcmd_chanmode_announce(&change
);
2918 static CHANSERV_FUNC(cmd_down
)
2920 struct mod_chanmode change
;
2922 mod_chanmode_init(&change
);
2924 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2925 if(!change
.args
[0].u
.member
)
2928 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2932 if(!change
.args
[0].u
.member
->modes
)
2935 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2939 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2940 modcmd_chanmode_announce(&change
);
2944 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
)
2946 struct userData
*cList
;
2948 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2950 if(IsSuspended(cList
->channel
)
2951 || IsUserSuspended(cList
)
2952 || !GetUserMode(cList
->channel
->channel
, user
))
2955 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2961 static CHANSERV_FUNC(cmd_upall
)
2963 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2966 static CHANSERV_FUNC(cmd_downall
)
2968 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2971 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2972 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2975 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
)
2977 unsigned int ii
, valid
;
2978 struct userNode
*victim
;
2979 struct mod_chanmode
*change
;
2981 change
= mod_chanmode_alloc(argc
- 1);
2983 for(ii
=valid
=0; ++ii
< argc
; )
2985 if(!(victim
= GetUserH(argv
[ii
])))
2987 change
->args
[valid
].mode
= mode
;
2988 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2989 if(!change
->args
[valid
].u
.member
)
2991 if(validate
&& !validate(user
, channel
, victim
))
2996 change
->argc
= valid
;
2997 if(valid
< (argc
-1))
2998 reply("CSMSG_PROCESS_FAILED");
3001 modcmd_chanmode_announce(change
);
3002 reply(action
, channel
->name
);
3004 mod_chanmode_free(change
);
3008 static CHANSERV_FUNC(cmd_op
)
3010 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
3013 static CHANSERV_FUNC(cmd_hop
)
3015 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
3018 static CHANSERV_FUNC(cmd_deop
)
3020 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
3023 static CHANSERV_FUNC(cmd_dehop
)
3025 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
3028 static CHANSERV_FUNC(cmd_voice
)
3030 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
3033 static CHANSERV_FUNC(cmd_devoice
)
3035 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
3039 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, unsigned int *victimCount
, struct modeNode
**victims
)
3045 for(ii
=0; ii
<channel
->members
.used
; ii
++)
3047 struct modeNode
*mn
= channel
->members
.list
[ii
];
3049 if(IsService(mn
->user
))
3052 if(!user_matches_glob(mn
->user
, ban
, MATCH_USENICK
| MATCH_VISIBLE
))
3055 if(protect_user(mn
->user
, user
, channel
->channel_info
))
3059 victims
[(*victimCount
)++] = mn
;
3065 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3067 struct userNode
*victim
;
3068 struct modeNode
**victims
;
3069 unsigned int offset
, n
, victimCount
, duration
= 0;
3070 char *reason
= "Bye.", *ban
, *name
;
3071 char interval
[INTERVALLEN
];
3073 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3074 REQUIRE_PARAMS(offset
);
3077 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3078 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3080 /* Truncate the reason to a length of TOPICLEN, as
3081 the ircd does; however, leave room for an ellipsis
3082 and the kicker's nick. */
3083 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3087 if((victim
= GetUserH(argv
[1])))
3089 victims
= alloca(sizeof(victims
[0]));
3090 victims
[0] = GetUserMode(channel
, victim
);
3091 /* XXX: The comparison with ACTION_KICK is just because all
3092 * other actions can work on users outside the channel, and we
3093 * want to allow those (e.g. unbans) in that case. If we add
3094 * some other ejection action for in-channel users, change
3096 victimCount
= victims
[0] ? 1 : 0;
3098 if(IsService(victim
))
3101 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3105 if((action
== ACTION_KICK
) && !victimCount
)
3108 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3112 if(protect_user(victim
, user
, channel
->channel_info
))
3114 // This translates to send_message(user, cmd->parent->bot, ...)
3115 // if user is x3 (ctcp action) cmd is null and segfault.
3117 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3121 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3122 name
= victim
->nick
;
3126 if(!is_ircmask(argv
[1]))
3129 reply("MSG_NICK_UNKNOWN", argv
[1]);
3133 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3135 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3138 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3141 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3142 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3144 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3145 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3146 some creativity, but its not x3's job to be the ban censor anyway. */
3147 if(is_overmask(argv
[1]))
3150 reply("CSMSG_LAME_MASK", argv
[1]);
3154 if((action
== ACTION_KICK
) && (victimCount
== 0))
3157 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3161 name
= ban
= strdup(argv
[1]);
3164 /* Truncate the ban in place if necessary; we must ensure
3165 that 'ban' is a valid ban mask before sanitizing it. */
3166 sanitize_ircmask(ban
);
3168 if(action
& ACTION_ADD_LAMER
)
3170 struct banData
*bData
, *next
;
3172 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3175 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3180 if(action
& ACTION_ADD_TIMED_LAMER
)
3182 duration
= ParseInterval(argv
[2]);
3187 reply("CSMSG_DURATION_TOO_LOW");
3191 else if(duration
> (86400 * 365 * 2))
3194 reply("CSMSG_DURATION_TOO_HIGH");
3201 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3203 if(match_ircglobs(bData
->mask
, ban
))
3205 int exact
= !irccasecmp(bData
->mask
, ban
);
3207 /* The ban is redundant; there is already a ban
3208 with the same effect in place. */
3212 free(bData
->reason
);
3213 bData
->reason
= strdup(reason
);
3214 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3216 reply("CSMSG_REASON_CHANGE", ban
);
3220 if(exact
&& bData
->expires
)
3224 /* If the ban matches an existing one exactly,
3225 extend the expiration time if the provided
3226 duration is longer. */
3227 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3229 bData
->expires
= now
+ duration
;
3240 /* Delete the expiration timeq entry and
3241 requeue if necessary. */
3242 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3245 timeq_add(bData
->expires
, expire_ban
, bData
);
3249 /* automated kickban, dont reply */
3252 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3254 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3260 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3267 if(match_ircglobs(ban
, bData
->mask
))
3269 /* The ban we are adding makes previously existing
3270 bans redundant; silently remove them. */
3271 del_channel_ban(bData
);
3275 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
);
3277 name
= ban
= strdup(bData
->mask
);
3281 /* WHAT DOES THIS DO?? -Rubin */
3282 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3284 extern const char *hidden_host_suffix
;
3285 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3287 unsigned int l1
, l2
;
3290 l2
= strlen(old_name
);
3293 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3295 new_mask
= malloc(MAXLEN
);
3296 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3298 name
= ban
= new_mask
;
3303 if(action
& ACTION_BAN
)
3305 unsigned int exists
;
3306 struct mod_chanmode
*change
;
3308 if(channel
->banlist
.used
>= MAXBANS
)
3311 reply("CSMSG_BANLIST_FULL", channel
->name
);
3316 exists
= ChannelBanExists(channel
, ban
);
3317 change
= mod_chanmode_alloc(victimCount
+ 1);
3318 for(n
= 0; n
< victimCount
; ++n
)
3320 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3321 change
->args
[n
].u
.member
= victims
[n
];
3325 change
->args
[n
].mode
= MODE_BAN
;
3326 change
->args
[n
++].u
.hostmask
= ban
;
3330 modcmd_chanmode_announce(change
);
3332 mod_chanmode_announce(chanserv
, channel
, change
);
3333 mod_chanmode_free(change
);
3335 if(exists
&& (action
== ACTION_BAN
))
3338 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3344 if(action
& ACTION_KICK
)
3346 char kick_reason
[MAXLEN
];
3347 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3349 for(n
= 0; n
< victimCount
; n
++)
3350 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3355 /* No response, since it was automated. */
3357 else if(action
& ACTION_ADD_LAMER
)
3360 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3362 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3364 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3365 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3366 else if(action
& ACTION_BAN
)
3367 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3368 else if(action
& ACTION_KICK
&& victimCount
)
3369 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3375 static CHANSERV_FUNC(cmd_kickban
)
3377 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3380 static CHANSERV_FUNC(cmd_kick
)
3382 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3385 static CHANSERV_FUNC(cmd_ban
)
3387 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3390 static CHANSERV_FUNC(cmd_addlamer
)
3392 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3395 static CHANSERV_FUNC(cmd_addtimedlamer
)
3397 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3400 static struct mod_chanmode
*
3401 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3403 struct mod_chanmode
*change
;
3404 unsigned char *match
;
3405 unsigned int ii
, count
;
3407 match
= alloca(bans
->used
);
3410 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3412 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
,
3413 MATCH_USENICK
| MATCH_VISIBLE
);
3420 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3422 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3429 change
= mod_chanmode_alloc(count
);
3430 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3434 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3435 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3437 assert(count
== change
->argc
);
3441 void expire_bans(UNUSED_ARG(void* data
)) /* Real bans, not lamers */
3443 unsigned int jj
, ii
, count
;
3445 struct chanData
*channel
;
3447 struct mod_chanmode
*change
;
3449 log_module(CS_LOG
, LOG_DEBUG
, "Checking for expired bans");
3450 /* Walk through every channel */
3451 for(channel
= channelList
; channel
; channel
= channel
->next
) {
3452 switch(channel
->chOpts
[chBanTimeout
])
3454 default: case '0': continue; /* Dont remove bans in this chan */
3455 case '1': bantimeout
= now
- (10 * 60); break; /* 10 minutes */
3456 case '2': bantimeout
= now
- (2 * 60 * 60); break; /* 2 hours */
3457 case '3': bantimeout
= now
- (4 * 60 * 60); break; /* 4 hours */
3458 case '4': bantimeout
= now
- (24 * 60 * 60); break; /* 24 hours */
3459 case '5': bantimeout
= now
- (7 * 24 * 60 * 60); break; /* 1 week */
3462 /* First find out how many bans were going to unset */
3463 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3464 if(channel
->channel
->banlist
.list
[jj
]->set
< bantimeout
)
3468 /* At least one ban, so setup a removal */
3469 change
= mod_chanmode_alloc(count
);
3471 /* Walk over every ban in this channel.. */
3472 for (jj
=0; jj
< channel
->channel
->banlist
.used
; ++jj
) {
3473 bn
= channel
->channel
->banlist
.list
[jj
];
3474 if (bn
->set
< bantimeout
) {
3475 log_module(CS_LOG
, LOG_DEBUG
, "Removing ban %s from %s", bn
->ban
, channel
->channel
->name
);
3477 /* Add this ban to the mode change */
3478 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3479 change
->args
[ii
].u
.hostmask
= strdup(bn
->ban
);
3481 /* Pull this ban out of the list */
3482 banList_remove(&(channel
->channel
->banlist
), bn
);
3487 /* Send the modes to IRC */
3488 mod_chanmode_announce(chanserv
, channel
->channel
, change
);
3490 /* free memory from strdup above */
3491 for(ii
= 0; ii
< count
; ++ii
)
3492 free((char*)change
->args
[ii
].u
.hostmask
);
3494 mod_chanmode_free(change
);
3497 /* Set this function to run again */
3498 if(chanserv_conf
.ban_timeout_frequency
)
3499 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
3504 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3506 struct userNode
*actee
;
3512 /* may want to allow a comma delimited list of users... */
3513 if(!(actee
= GetUserH(argv
[1])))
3515 if(!is_ircmask(argv
[1]))
3517 reply("MSG_NICK_UNKNOWN", argv
[1]);
3521 mask
= strdup(argv
[1]);
3524 /* We don't sanitize the mask here because ircu
3526 if(action
& ACTION_UNBAN
)
3528 struct mod_chanmode
*change
;
3529 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3534 modcmd_chanmode_announce(change
);
3535 for(ii
= 0; ii
< change
->argc
; ++ii
)
3536 free((char*)change
->args
[ii
].u
.hostmask
);
3537 mod_chanmode_free(change
);
3542 if(action
& ACTION_DEL_LAMER
)
3544 struct banData
*ban
, *next
;
3546 ban
= channel
->channel_info
->bans
; /* lamers */
3550 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, MATCH_USENICK
| MATCH_VISIBLE
);
3553 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3558 del_channel_ban(ban
);
3565 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3567 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3573 static CHANSERV_FUNC(cmd_unban
)
3575 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3578 static CHANSERV_FUNC(cmd_dellamer
)
3580 /* it doesn't necessarily have to remove the channel ban - may want
3581 to make that an option. */
3582 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3585 static CHANSERV_FUNC(cmd_unbanme
)
3587 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3588 long flags
= ACTION_UNBAN
;
3590 /* remove permanent bans if the user has the proper access. */
3591 if(uData
->access
>= UL_MANAGER
)
3592 flags
|= ACTION_DEL_LAMER
;
3594 argv
[1] = user
->nick
;
3595 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3598 static CHANSERV_FUNC(cmd_unbanall
)
3600 struct mod_chanmode
*change
;
3603 if(!channel
->banlist
.used
)
3605 reply("CSMSG_NO_BANS", channel
->name
);
3609 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3610 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3612 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3613 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3615 modcmd_chanmode_announce(change
);
3616 for(ii
= 0; ii
< change
->argc
; ++ii
)
3617 free((char*)change
->args
[ii
].u
.hostmask
);
3618 mod_chanmode_free(change
);
3619 reply("CSMSG_BANS_REMOVED", channel
->name
);
3623 static CHANSERV_FUNC(cmd_open
)
3625 struct mod_chanmode
*change
;
3628 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3630 change
= mod_chanmode_alloc(0);
3631 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3632 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3633 && channel
->channel_info
->modes
.modes_set
)
3634 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3635 modcmd_chanmode_announce(change
);
3636 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3637 for(ii
= 0; ii
< change
->argc
; ++ii
)
3638 free((char*)change
->args
[ii
].u
.hostmask
);
3639 mod_chanmode_free(change
);
3643 static CHANSERV_FUNC(cmd_myaccess
)
3645 static struct string_buffer sbuf
;
3646 struct handle_info
*target_handle
;
3647 struct userData
*uData
;
3650 target_handle
= user
->handle_info
;
3651 else if(!IsHelping(user
))
3653 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3656 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3659 if(!target_handle
->channels
)
3661 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3665 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3666 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3668 struct chanData
*cData
= uData
->channel
;
3670 if(uData
->access
> UL_OWNER
)
3672 if(IsProtected(cData
)
3673 && (target_handle
!= user
->handle_info
)
3674 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3677 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3678 if(uData
->flags
== USER_AUTO_OP
)
3679 string_buffer_append(&sbuf
, ',');
3680 if(IsUserSuspended(uData
))
3681 string_buffer_append(&sbuf
, 's');
3682 if(IsUserAutoOp(uData
))
3684 if(uData
->access
>= UL_OP
)
3685 string_buffer_append(&sbuf
, 'o');
3686 else if(uData
->access
>= UL_HALFOP
)
3687 string_buffer_append(&sbuf
, 'h');
3688 else if(uData
->access
>= UL_PEON
)
3689 string_buffer_append(&sbuf
, 'v');
3691 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3692 string_buffer_append(&sbuf
, 'i');
3694 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3696 string_buffer_append_string(&sbuf
, ")]");
3697 string_buffer_append(&sbuf
, '\0');
3698 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3704 static CHANSERV_FUNC(cmd_access
)
3706 struct userNode
*target
;
3707 struct handle_info
*target_handle
;
3708 struct userData
*uData
;
3710 char prefix
[MAXLEN
];
3715 target_handle
= target
->handle_info
;
3717 else if((target
= GetUserH(argv
[1])))
3719 target_handle
= target
->handle_info
;
3721 else if(argv
[1][0] == '*')
3723 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3725 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3731 reply("MSG_NICK_UNKNOWN", argv
[1]);
3735 assert(target
|| target_handle
);
3737 if(target
== chanserv
)
3739 reply("CSMSG_IS_CHANSERV");
3747 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3752 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3755 reply("MSG_AUTHENTICATE");
3761 const char *epithet
= NULL
, *type
= NULL
;
3764 epithet
= chanserv_conf
.irc_operator_epithet
;
3767 else if(IsNetworkHelper(target
))
3769 epithet
= chanserv_conf
.network_helper_epithet
;
3770 type
= "network helper";
3772 else if(IsSupportHelper(target
))
3774 epithet
= chanserv_conf
.support_helper_epithet
;
3775 type
= "support helper";
3779 if(target_handle
->epithet
)
3780 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3782 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3784 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3788 sprintf(prefix
, "%s", target_handle
->handle
);
3791 if(!channel
->channel_info
)
3793 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3797 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3798 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3799 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3801 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3802 /* To prevent possible information leaks, only show infolines
3803 * if the requestor is in the channel or it's their own
3805 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3807 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3809 /* Likewise, only say it's suspended if the user has active
3810 * access in that channel or it's their own entry. */
3811 if(IsUserSuspended(uData
)
3812 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3813 || (user
->handle_info
== uData
->handle
)))
3815 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3820 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3826 /* This is never used...
3828 zoot_list(struct listData *list)
3830 struct userData *uData;
3831 unsigned int start, curr, highest, lowest;
3832 struct helpfile_table tmp_table;
3833 const char **temp, *msg;
3835 if(list->table.length == 1)
3838 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);
3840 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));
3841 msg = user_find_message(list->user, "MSG_NONE");
3842 send_message_type(4, list->user, list->bot, " %s", msg);
3844 tmp_table.width = list->table.width;
3845 tmp_table.flags = list->table.flags;
3846 list->table.contents[0][0] = " ";
3847 highest = list->highest;
3848 if(list->lowest != 0)
3849 lowest = list->lowest;
3850 else if(highest < 100)
3853 lowest = highest - 100;
3854 for(start = curr = 1; curr < list->table.length; )
3856 uData = list->users[curr-1];
3857 list->table.contents[curr++][0] = " ";
3858 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3861 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);
3863 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));
3864 temp = list->table.contents[--start];
3865 list->table.contents[start] = list->table.contents[0];
3866 tmp_table.contents = list->table.contents + start;
3867 tmp_table.length = curr - start;
3868 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3869 list->table.contents[start] = temp;
3871 highest = lowest - 1;
3872 lowest = (highest < 100) ? 0 : (highest - 99);
3879 normal_list(struct listData
*list
)
3883 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
);
3885 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
));
3886 if(list
->table
.length
== 1)
3888 msg
= user_find_message(list
->user
, "MSG_NONE");
3889 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3892 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3895 /* if these need changed, uncomment and customize
3897 clean_list(struct listData *list)
3901 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);
3903 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));
3904 if(list->table.length == 1)
3906 msg = user_find_message(list->user, "MSG_NONE");
3907 send_message_type(4, list->user, list->bot, " %s", msg);
3910 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3914 advanced_list(struct listData *list)
3918 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);
3920 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));
3921 if(list->table.length == 1)
3923 msg = user_find_message(list->user, "MSG_NONE");
3924 send_message_type(4, list->user, list->bot, " %s", msg);
3927 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3933 userData_access_comp(const void *arg_a
, const void *arg_b
)
3935 const struct userData
*a
= *(struct userData
**)arg_a
;
3936 const struct userData
*b
= *(struct userData
**)arg_b
;
3938 if(a
->access
!= b
->access
)
3939 res
= b
->access
- a
->access
;
3941 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3946 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3948 void (*send_list
)(struct listData
*);
3949 struct userData
*uData
;
3950 struct listData lData
;
3951 unsigned int matches
;
3957 lData
.bot
= cmd
->parent
->bot
;
3958 lData
.channel
= channel
;
3959 lData
.lowest
= lowest
;
3960 lData
.highest
= highest
;
3961 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3962 send_list
= normal_list
;
3963 /* What does the following line do exactly?? */
3964 /*(void)zoot_list; ** since it doesn't show user levels */
3967 if(user->handle_info)
3969 switch(user->handle_info->userlist_style)
3971 case HI_STYLE_CLEAN:
3972 send_list = clean_list;
3974 case HI_STYLE_ADVANCED:
3975 send_list = advanced_list;
3977 case HI_STYLE_NORMAL:
3979 send_list = normal_list;
3984 send_list
= normal_list
;
3986 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3988 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3990 if((uData
->access
< lowest
)
3991 || (uData
->access
> highest
)
3992 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3994 lData
.users
[matches
++] = uData
;
3996 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3998 lData
.table
.length
= matches
+1;
3999 lData
.table
.flags
= TABLE_NO_FREE
;
4000 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
4002 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4003 lData
.table
.width
= 5; /* with level = 5 */
4005 lData
.table
.width
= 4; /* without = 4 */
4006 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4007 lData
.table
.contents
[0] = ary
;
4008 ary
[i
++] = "Access";
4009 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4010 ary
[i
++] = "Level"; /* Only on advanced view */
4011 ary
[i
++] = "Account";
4012 ary
[i
] = "Last Seen";
4014 ary
[i
++] = "Status";
4015 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4017 struct userData
*uData
= lData
.users
[matches
-1];
4018 char seen
[INTERVALLEN
];
4021 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
4022 lData
.table
.contents
[matches
] = ary
;
4023 ary
[i
++] = user_level_name_from_level(uData
->access
);
4024 if(user
->handle_info
&& user
->handle_info
->userlist_style
== HI_STYLE_ADVANCED
)
4025 ary
[i
++] = strtab(uData
->access
);
4026 ary
[i
++] = uData
->handle
->handle
;
4029 else if(!uData
->seen
)
4032 ary
[i
] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
4033 ary
[i
] = strdup(ary
[i
]);
4035 if(IsUserSuspended(uData
))
4036 ary
[i
++] = "Suspended";
4037 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
4038 ary
[i
++] = "Vacation";
4040 ary
[i
++] = "Normal";
4043 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
4045 /* Free strdup above */
4046 free((char*)lData
.table
.contents
[matches
][seen_index
]);
4047 free(lData
.table
.contents
[matches
]);
4049 free(lData
.table
.contents
[0]);
4050 free(lData
.table
.contents
);
4054 /* Remove this now that debugging is over? or improve it for
4055 * users? Would it be better tied into USERS somehow? -Rubin */
4056 static CHANSERV_FUNC(cmd_pending
)
4058 struct adduserPending
*ap
;
4059 reply("CSMSG_ADDUSER_PENDING_HEADER");
4060 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4062 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
4063 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
4064 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4068 static CHANSERV_FUNC(cmd_users
)
4070 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
4073 static CHANSERV_FUNC(cmd_wlist
)
4075 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
4078 static CHANSERV_FUNC(cmd_clist
)
4080 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
4083 static CHANSERV_FUNC(cmd_mlist
)
4085 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
4088 static CHANSERV_FUNC(cmd_olist
)
4090 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
4093 static CHANSERV_FUNC(cmd_hlist
)
4095 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
4098 static CHANSERV_FUNC(cmd_plist
)
4100 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
4103 static CHANSERV_FUNC(cmd_lamers
)
4105 struct helpfile_table tbl
;
4106 unsigned int matches
= 0, timed
= 0, ii
;
4107 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
4108 const char *msg_never
, *triggered
, *expires
;
4109 struct banData
*ban
, **bans
; /* lamers */
4116 reply("CSMSG_LAMERS_HEADER", channel
->name
);
4117 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
4120 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
4122 if(search
&& !match_ircglobs(search
, ban
->mask
))
4124 bans
[matches
++] = ban
;
4129 tbl
.length
= matches
+ 1;
4130 tbl
.width
= 4 + timed
;
4132 tbl
.flags
= TABLE_NO_FREE
;
4133 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
4134 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4135 tbl
.contents
[0][0] = "Mask";
4136 tbl
.contents
[0][1] = "Set By";
4137 tbl
.contents
[0][2] = "Triggered";
4140 tbl
.contents
[0][3] = "Expires";
4141 tbl
.contents
[0][4] = "Reason";
4144 tbl
.contents
[0][3] = "Reason";
4147 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4149 free(tbl
.contents
[0]);
4154 msg_never
= user_find_message(user
, "MSG_NEVER");
4155 for(ii
= 0; ii
< matches
; )
4161 else if(ban
->expires
)
4162 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
4164 expires
= msg_never
;
4167 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
4169 triggered
= msg_never
;
4171 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
4172 tbl
.contents
[ii
][0] = ban
->mask
;
4173 tbl
.contents
[ii
][1] = ban
->owner
;
4174 tbl
.contents
[ii
][2] = strdup(triggered
);
4177 tbl
.contents
[ii
][3] = strdup(expires
);
4178 tbl
.contents
[ii
][4] = ban
->reason
;
4181 tbl
.contents
[ii
][3] = ban
->reason
;
4183 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
4184 /* reply("MSG_MATCH_COUNT", matches); */
4185 for(ii
= 1; ii
< tbl
.length
; ++ii
)
4187 free((char*)tbl
.contents
[ii
][2]);
4189 free((char*)tbl
.contents
[ii
][3]);
4190 free(tbl
.contents
[ii
]);
4192 free(tbl
.contents
[0]);
4199 * return + if the user does NOT have the right to set the topic, and
4200 * the topic is changed.
4203 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4205 struct chanData
*cData
= channel
->channel_info
;
4206 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4208 else if(cData
->topic
)
4209 return irccasecmp(new_topic
, cData
->topic
);
4216 * Makes a givin topic fit into a givin topic mask and returns
4219 * topic_mask - the mask to conform to
4220 * topic - the topic to make conform
4221 * new_topic - the pre-allocated char* to put the new topic into
4223 * modifies: new_topic
4226 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4228 //char *topic_mask = cData->topic_mask;
4230 int pos
=0, starpos
=-1, dpos
=0, len
;
4232 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4239 strcpy(new_topic
, "");
4242 len
= strlen(topic
);
4243 if((dpos
+ len
) > TOPICLEN
)
4244 len
= TOPICLEN
+ 1 - dpos
;
4245 memcpy(new_topic
+dpos
, topic
, len
);
4249 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4250 default: new_topic
[dpos
++] = tchar
; break;
4253 if((dpos
> TOPICLEN
) || tchar
)
4255 strcpy(new_topic
, "");
4258 new_topic
[dpos
] = 0;
4262 static CHANSERV_FUNC(cmd_topic
)
4264 struct chanData
*cData
;
4268 #ifdef WITH_PROTOCOL_P10
4272 cData
= channel
->channel_info
;
4277 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, cData
->topic
, 1);
4278 reply("CSMSG_TOPIC_SET", cData
->topic
);
4282 reply("CSMSG_NO_TOPIC", channel
->name
);
4286 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4287 /* If they say "!topic *", use an empty topic. */
4288 if((topic
[0] == '*') && (topic
[1] == 0))
4291 if(bad_topic(channel
, user
, topic
))
4293 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4298 /* If there is a topicmask set, and the new topic doesnt match, make it */
4299 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4301 char *topic_mask
= cData
->topic_mask
;
4302 char new_topic
[TOPICLEN
+1];
4304 /* make a new topic fitting mask */
4305 conform_topic(topic_mask
, topic
, new_topic
);
4308 /* Topic couldnt fit into mask, was too long */
4309 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4310 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4313 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, new_topic
, 1);
4315 else /* No mask set, just set the topic */
4316 SetChannelTopic(channel
, chanserv
, p10
? user
: chanserv
, topic
, 1);
4319 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4321 /* Grab the topic and save it as the default topic. */
4323 cData
->topic
= strdup(channel
->topic
);
4329 static CHANSERV_FUNC(cmd_mode
)
4331 struct userData
*uData
;
4332 struct mod_chanmode
*change
;
4337 change
= &channel
->channel_info
->modes
;
4338 if(change
->modes_set
|| change
->modes_clear
) {
4339 modcmd_chanmode_announce(change
);
4340 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4342 reply("CSMSG_NO_MODES", channel
->name
);
4346 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4348 base_oplevel
= MAXOPLEVEL
;
4349 else if (uData
->access
>= UL_OWNER
)
4352 base_oplevel
= 1 + UL_OWNER
- uData
->access
;
4353 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
, base_oplevel
);
4357 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4361 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4362 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4365 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4366 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4370 modcmd_chanmode_announce(change
);
4371 mod_chanmode_free(change
);
4372 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4376 static CHANSERV_FUNC(cmd_invite
)
4378 struct userData
*uData
;
4379 struct userNode
*invite
;
4381 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4385 if(!(invite
= GetUserH(argv
[1])))
4387 reply("MSG_NICK_UNKNOWN", argv
[1]);
4394 if(GetUserMode(channel
, invite
))
4396 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4404 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4405 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4408 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4410 irc_invite(chanserv
, invite
, channel
);
4412 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4417 static CHANSERV_FUNC(cmd_inviteme
)
4419 if(GetUserMode(channel
, user
))
4421 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4424 if(channel
->channel_info
4425 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4427 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4430 irc_invite(cmd
->parent
->bot
, user
, channel
);
4435 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4438 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4440 /* We display things based on two dimensions:
4441 * - Issue time: present or absent
4442 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4443 * (in order of precedence, so something both expired and revoked
4444 * only counts as revoked)
4446 combo
= (suspended
->issued
? 4 : 0)
4447 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4449 case 0: /* no issue time, indefinite expiration */
4450 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4452 case 1: /* no issue time, expires in future */
4453 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4454 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4456 case 2: /* no issue time, expired */
4457 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4458 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4460 case 3: /* no issue time, revoked */
4461 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4462 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4464 case 4: /* issue time set, indefinite expiration */
4465 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4466 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4468 case 5: /* issue time set, expires in future */
4469 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4470 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4471 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4473 case 6: /* issue time set, expired */
4474 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4475 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4476 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4478 case 7: /* issue time set, revoked */
4479 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4480 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4481 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4484 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4490 show_giveownership_info(struct svccmd
*cmd
, struct userNode
*user
, struct giveownership
*giveownership
)
4493 const char *fmt
= "%a %b %d %H:%M %Y";
4494 strftime(buf
, sizeof(buf
), fmt
, localtime(&giveownership
->issued
));
4496 if(giveownership
->staff_issuer
)
4498 if(giveownership
->reason
)
4499 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership
->old_owner
,
4500 giveownership
->target
, giveownership
->target_access
,
4501 giveownership
->staff_issuer
, buf
, giveownership
->reason
);
4503 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership
->old_owner
,
4504 giveownership
->target
, giveownership
->target_access
,
4505 giveownership
->staff_issuer
, buf
);
4509 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership
->old_owner
, giveownership
->target
, giveownership
->target_access
, buf
);
4514 static CHANSERV_FUNC(cmd_info
)
4516 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4517 struct userData
*uData
, *owner
;
4518 struct chanData
*cData
;
4519 struct do_not_register
*dnr
;
4524 cData
= channel
->channel_info
;
4525 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4526 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4529 uData
= GetChannelUser(cData
, user
->handle_info
);
4530 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4532 mod_chanmode_format(&cData
->modes
, modes
);
4533 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4534 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4537 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4541 note
= iter_data(it
);
4542 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4545 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4546 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4549 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4550 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4551 if(owner
->access
== UL_OWNER
)
4552 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4553 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4554 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4555 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4557 privileged
= IsStaff(user
);
4559 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4560 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4561 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4563 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4564 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4566 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4568 struct suspended
*suspended
;
4569 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4570 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4571 show_suspension_info(cmd
, user
, suspended
);
4573 else if(IsSuspended(cData
))
4575 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4576 show_suspension_info(cmd
, user
, cData
->suspended
);
4578 if(cData
->giveownership
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsStaff(user
)))
4580 struct giveownership
*giveownership
;
4581 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel
->name
);
4582 for(giveownership
= cData
->giveownership
; giveownership
; giveownership
= giveownership
->previous
)
4583 show_giveownership_info(cmd
, user
, giveownership
);
4585 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4586 reply("CSMSG_CHANNEL_END");
4588 reply("CSMSG_CHANNEL_END_CLEAN");
4592 static CHANSERV_FUNC(cmd_netinfo
)
4594 extern time_t boot_time
;
4595 extern unsigned long burst_length
;
4596 char interval
[INTERVALLEN
];
4598 reply("CSMSG_NETWORK_INFO");
4599 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4600 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4601 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4602 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4603 reply("CSMSG_NETWORK_LAMERS", banCount
);
4604 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4605 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4606 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4611 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4613 struct helpfile_table table
;
4615 struct userNode
*user
;
4620 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4621 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4622 for(nn
=0; nn
<list
->used
; nn
++)
4624 user
= list
->list
[nn
];
4625 if(user
->modes
& skip_flags
)
4629 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4632 nick
= alloca(strlen(user
->nick
)+3);
4633 sprintf(nick
, "(%s)", user
->nick
);
4637 table
.contents
[table
.length
][0] = nick
;
4640 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4643 static CHANSERV_FUNC(cmd_ircops
)
4645 reply("CSMSG_STAFF_OPERS");
4646 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4650 static CHANSERV_FUNC(cmd_helpers
)
4652 reply("CSMSG_STAFF_HELPERS");
4653 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4657 static CHANSERV_FUNC(cmd_staff
)
4659 reply("CSMSG_NETWORK_STAFF");
4660 cmd_ircops(CSFUNC_ARGS
);
4661 cmd_helpers(CSFUNC_ARGS
);
4665 static CHANSERV_FUNC(cmd_peek
)
4667 struct modeNode
*mn
;
4668 char modes
[MODELEN
];
4670 struct helpfile_table table
;
4672 irc_make_chanmode(channel
, modes
);
4674 reply("CSMSG_PEEK_INFO", channel
->name
);
4675 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
4677 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4678 reply("CSMSG_PEEK_MODES", modes
);
4679 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4683 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4684 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4685 for(n
= 0; n
< channel
->members
.used
; n
++)
4687 mn
= channel
->members
.list
[n
];
4688 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4690 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4691 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4696 reply("CSMSG_PEEK_OPS");
4697 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4700 reply("CSMSG_PEEK_NO_OPS");
4701 reply("CSMSG_PEEK_END");
4705 static MODCMD_FUNC(cmd_wipeinfo
)
4707 struct handle_info
*victim
;
4708 struct userData
*ud
, *actor
;
4711 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4712 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4714 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4716 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4719 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4721 reply("MSG_USER_OUTRANKED", victim
->handle
);
4727 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4731 static CHANSERV_FUNC(cmd_resync
)
4733 struct mod_chanmode
*changes
;
4734 struct chanData
*cData
= channel
->channel_info
;
4735 unsigned int ii
, used
;
4737 /* 6 = worst case -ovh+ovh on everyone */
4738 changes
= mod_chanmode_alloc(channel
->members
.used
* 6);
4739 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4741 struct modeNode
*mn
= channel
->members
.list
[ii
];
4742 struct userData
*uData
;
4744 if(IsService(mn
->user
))
4748 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4750 /* If the channel is in no-mode mode, de-mode EVERYONE */
4751 if(cData
->chOpts
[chAutomode
] == 'n')
4755 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4756 changes
->args
[used
++].u
.member
= mn
;
4759 else /* Give various userlevels their modes.. */
4761 if(uData
&& uData
->access
>= UL_OP
)
4763 if(!(mn
->modes
& MODE_CHANOP
))
4765 changes
->args
[used
].mode
= MODE_CHANOP
;
4766 changes
->args
[used
++].u
.member
= mn
;
4769 else if(uData
&& uData
->access
>= UL_HALFOP
)
4771 if(mn
->modes
& MODE_CHANOP
)
4773 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4774 changes
->args
[used
++].u
.member
= mn
;
4776 if(!(mn
->modes
& MODE_HALFOP
))
4778 changes
->args
[used
].mode
= MODE_HALFOP
;
4779 changes
->args
[used
++].u
.member
= mn
;
4782 else if(uData
&& uData
->access
>= UL_PEON
)
4784 if(mn
->modes
& MODE_CHANOP
)
4786 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4787 changes
->args
[used
++].u
.member
= mn
;
4789 if(mn
->modes
& MODE_HALFOP
)
4791 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4792 changes
->args
[used
++].u
.member
= mn
;
4794 /* Don't voice peons if were in mode m */
4795 if( cData
->chOpts
[chAutomode
] == 'm')
4797 if(mn
->modes
& MODE_VOICE
)
4799 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_VOICE
;
4800 changes
->args
[used
++].u
.member
= mn
;
4803 /* otherwise, make user they do have voice */
4804 else if(!(mn
->modes
& MODE_VOICE
))
4806 changes
->args
[used
].mode
= MODE_VOICE
;
4807 changes
->args
[used
++].u
.member
= mn
;
4810 else /* They arnt on the userlist.. */
4812 /* If we voice everyone, but they dont.. */
4813 if(cData
->chOpts
[chAutomode
] == 'v')
4815 /* Remove anything except v */
4816 if(mn
->modes
& ~MODE_VOICE
)
4818 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4819 changes
->args
[used
++].u
.member
= mn
;
4822 if(!(mn
->modes
& MODE_VOICE
))
4824 changes
->args
[used
].mode
= MODE_VOICE
;
4825 changes
->args
[used
++].u
.member
= mn
;
4828 /* If we hop everyone, but they dont.. */
4829 else if(cData
->chOpts
[chAutomode
] == 'h')
4831 /* Remove anything except h */
4832 if(mn
->modes
& ~MODE_HALFOP
)
4834 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_HALFOP
);
4835 changes
->args
[used
++].u
.member
= mn
;
4838 if(!(mn
->modes
& MODE_HALFOP
))
4840 changes
->args
[used
].mode
= MODE_HALFOP
;
4841 changes
->args
[used
++].u
.member
= mn
;
4844 /* If we op everyone, but they dont.. */
4845 else if(cData
->chOpts
[chAutomode
] == 'o')
4847 /* Remove anything except h */
4848 if(mn
->modes
& ~MODE_CHANOP
)
4850 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4851 changes
->args
[used
++].u
.member
= mn
;
4854 if(!(mn
->modes
& MODE_CHANOP
))
4856 changes
->args
[used
].mode
= MODE_CHANOP
;
4857 changes
->args
[used
++].u
.member
= mn
;
4860 /* they have no excuse for having modes, de-everything them */
4865 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4866 changes
->args
[used
++].u
.member
= mn
;
4872 changes
->argc
= used
;
4873 modcmd_chanmode_announce(changes
);
4874 mod_chanmode_free(changes
);
4875 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4879 static CHANSERV_FUNC(cmd_seen
)
4881 struct userData
*uData
;
4882 struct handle_info
*handle
;
4883 char seen
[INTERVALLEN
];
4887 if(!irccasecmp(argv
[1], chanserv
->nick
))
4889 reply("CSMSG_IS_CHANSERV");
4893 if(!(handle
= get_handle_info(argv
[1])))
4895 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4899 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4901 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4906 reply("CSMSG_USER_PRESENT", handle
->handle
);
4907 else if(uData
->seen
)
4908 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4910 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4912 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4913 reply("CSMSG_USER_VACATION", handle
->handle
);
4918 static MODCMD_FUNC(cmd_names
)
4920 struct userNode
*targ
;
4921 struct userData
*targData
;
4922 unsigned int ii
, pos
;
4925 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4927 targ
= channel
->members
.list
[ii
]->user
;
4928 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4931 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4934 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4938 if(IsUserSuspended(targData
))
4940 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4943 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4944 reply("CSMSG_END_NAMES", channel
->name
);
4949 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4951 switch(ntype
->visible_type
)
4953 case NOTE_VIS_ALL
: return 1;
4954 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4955 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4960 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4962 struct userData
*uData
;
4964 switch(ntype
->set_access_type
)
4966 case NOTE_SET_CHANNEL_ACCESS
:
4967 if(!user
->handle_info
)
4969 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4971 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4972 case NOTE_SET_CHANNEL_SETTER
:
4973 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4974 case NOTE_SET_PRIVILEGED
: default:
4975 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4979 static CHANSERV_FUNC(cmd_note
)
4981 struct chanData
*cData
;
4983 struct note_type
*ntype
;
4985 cData
= channel
->channel_info
;
4988 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4992 /* If no arguments, show all visible notes for the channel. */
4998 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
5000 note
= iter_data(it
);
5001 if(!note_type_visible_to_user(cData
, note
->type
, user
))
5004 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
5005 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
5008 reply("CSMSG_NOTELIST_END", channel
->name
);
5010 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
5012 /* If one argument, show the named note. */
5015 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
5016 && note_type_visible_to_user(cData
, note
->type
, user
))
5018 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
5020 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
5021 && note_type_visible_to_user(NULL
, ntype
, user
))
5023 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
5028 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5032 /* Assume they're trying to set a note. */
5036 ntype
= dict_find(note_types
, argv
[1], NULL
);
5039 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
5042 else if(note_type_settable_by_user(channel
, ntype
, user
))
5044 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
5045 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
5046 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
5047 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
5048 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
5050 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
5052 /* The note is viewable to staff only, so return 0
5053 to keep the invocation from getting logged (or
5054 regular users can see it in !events). */
5060 reply("CSMSG_NO_ACCESS");
5067 static CHANSERV_FUNC(cmd_delnote
)
5072 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
5073 || !note_type_settable_by_user(channel
, note
->type
, user
))
5075 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
5078 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
5079 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
5083 static CHANSERV_FUNC(cmd_last
)
5089 numoflines
= (argc
> 1) ? atoi(argv
[1]) : 10;
5091 if(numoflines
< 1 || numoflines
> 200)
5093 reply("CSMSG_LAST_INVALID");
5096 ShowLog(user
, channel
, "*", "*", "*", "*", numoflines
);
5100 static CHANSERV_FUNC(cmd_events
)
5102 struct logSearch discrim
;
5103 struct logReport report
;
5104 unsigned int matches
, limit
;
5106 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
5107 if(limit
< 1 || limit
> 200)
5110 memset(&discrim
, 0, sizeof(discrim
));
5111 discrim
.masks
.bot
= chanserv
;
5112 discrim
.masks
.channel_name
= channel
->name
;
5114 discrim
.masks
.command
= argv
[2];
5115 discrim
.limit
= limit
;
5116 discrim
.max_time
= INT_MAX
;
5117 discrim
.severities
= 1 << LOG_COMMAND
;
5118 report
.reporter
= chanserv
;
5120 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
5121 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5123 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
5125 reply("MSG_MATCH_COUNT", matches
);
5127 reply("MSG_NO_MATCHES");
5131 static CHANSERV_FUNC(cmd_say
)
5137 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5138 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
5140 else if(GetUserH(argv
[1]))
5143 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5144 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
5148 reply("MSG_NOT_TARGET_NAME");
5154 static CHANSERV_FUNC(cmd_emote
)
5160 /* CTCP is so annoying. */
5161 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5162 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5164 else if(GetUserH(argv
[1]))
5166 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
5167 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
5171 reply("MSG_NOT_TARGET_NAME");
5177 struct channelList
*
5178 chanserv_support_channels(void)
5180 return &chanserv_conf
.support_channels
;
5183 static CHANSERV_FUNC(cmd_expire
)
5185 int channel_count
= registered_channels
;
5186 expire_channels(NULL
);
5187 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
5192 chanserv_expire_suspension(void *data
)
5194 struct suspended
*suspended
= data
;
5195 struct chanNode
*channel
;
5197 if(!suspended
->expires
|| (now
< suspended
->expires
))
5198 suspended
->revoked
= now
;
5199 channel
= suspended
->cData
->channel
;
5200 suspended
->cData
->channel
= channel
;
5201 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
5202 if(!IsOffChannel(suspended
->cData
))
5204 struct mod_chanmode change
;
5205 mod_chanmode_init(&change
);
5207 change
.args
[0].mode
= MODE_CHANOP
;
5208 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5209 mod_chanmode_announce(chanserv
, channel
, &change
);
5213 static CHANSERV_FUNC(cmd_csuspend
)
5215 struct suspended
*suspended
;
5216 char reason
[MAXLEN
];
5217 time_t expiry
, duration
;
5218 struct userData
*uData
;
5222 if(IsProtected(channel
->channel_info
))
5224 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
5228 if(argv
[1][0] == '!')
5230 else if(IsSuspended(channel
->channel_info
))
5232 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
5233 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
5237 if(!strcmp(argv
[1], "0"))
5239 else if((duration
= ParseInterval(argv
[1])))
5240 expiry
= now
+ duration
;
5243 reply("MSG_INVALID_DURATION", argv
[1]);
5247 unsplit_string(argv
+ 2, argc
- 2, reason
);
5249 suspended
= calloc(1, sizeof(*suspended
));
5250 suspended
->revoked
= 0;
5251 suspended
->issued
= now
;
5252 suspended
->suspender
= strdup(user
->handle_info
->handle
);
5253 suspended
->expires
= expiry
;
5254 suspended
->reason
= strdup(reason
);
5255 suspended
->cData
= channel
->channel_info
;
5256 suspended
->previous
= suspended
->cData
->suspended
;
5257 suspended
->cData
->suspended
= suspended
;
5259 if(suspended
->expires
)
5260 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
5262 if(IsSuspended(channel
->channel_info
))
5264 suspended
->previous
->revoked
= now
;
5265 if(suspended
->previous
->expires
)
5266 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
5267 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
5268 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5272 /* Mark all users in channel as absent. */
5273 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
5282 /* Mark the channel as suspended, then part. */
5283 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
5284 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
5285 reply("CSMSG_SUSPENDED", channel
->name
);
5286 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
5287 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5292 static CHANSERV_FUNC(cmd_cunsuspend
)
5294 struct suspended
*suspended
;
5295 char message
[MAXLEN
];
5297 if(!IsSuspended(channel
->channel_info
))
5299 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
5303 suspended
= channel
->channel_info
->suspended
;
5305 /* Expire the suspension and join ChanServ to the channel. */
5306 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
5307 chanserv_expire_suspension(suspended
);
5308 reply("CSMSG_UNSUSPENDED", channel
->name
);
5309 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
5310 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
5314 typedef struct chanservSearch
5322 unsigned long flags
;
5326 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
5329 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
5334 search
= malloc(sizeof(struct chanservSearch
));
5335 memset(search
, 0, sizeof(*search
));
5338 for(i
= 0; i
< argc
; i
++)
5340 /* Assume all criteria require arguments. */
5343 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5347 if(!irccasecmp(argv
[i
], "name"))
5348 search
->name
= argv
[++i
];
5349 else if(!irccasecmp(argv
[i
], "registrar"))
5350 search
->registrar
= argv
[++i
];
5351 else if(!irccasecmp(argv
[i
], "unvisited"))
5352 search
->unvisited
= ParseInterval(argv
[++i
]);
5353 else if(!irccasecmp(argv
[i
], "registered"))
5354 search
->registered
= ParseInterval(argv
[++i
]);
5355 else if(!irccasecmp(argv
[i
], "flags"))
5358 if(!irccasecmp(argv
[i
], "nodelete"))
5359 search
->flags
|= CHANNEL_NODELETE
;
5360 else if(!irccasecmp(argv
[i
], "suspended"))
5361 search
->flags
|= CHANNEL_SUSPENDED
;
5364 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5368 else if(!irccasecmp(argv
[i
], "limit"))
5369 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5372 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5377 if(search
->name
&& !strcmp(search
->name
, "*"))
5379 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5380 search
->registrar
= 0;
5389 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5391 const char *name
= channel
->channel
->name
;
5392 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5393 (search
->registrar
&& !channel
->registrar
) ||
5394 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5395 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5396 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5397 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5404 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5406 struct chanData
*channel
;
5407 unsigned int matches
= 0;
5409 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5411 if(!chanserv_channel_match(channel
, search
))
5421 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5426 search_print(struct chanData
*channel
, void *data
)
5428 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5431 static CHANSERV_FUNC(cmd_search
)
5434 unsigned int matches
;
5435 channel_search_func action
;
5439 if(!irccasecmp(argv
[1], "count"))
5440 action
= search_count
;
5441 else if(!irccasecmp(argv
[1], "print"))
5442 action
= search_print
;
5445 reply("CSMSG_ACTION_INVALID", argv
[1]);
5449 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5453 if(action
== search_count
)
5454 search
->limit
= INT_MAX
;
5456 if(action
== search_print
)
5458 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5459 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
5463 matches
= chanserv_channel_search(search
, action
, user
);
5466 reply("MSG_MATCH_COUNT", matches
);
5468 reply("MSG_NO_MATCHES");
5474 static CHANSERV_FUNC(cmd_unvisited
)
5476 struct chanData
*cData
;
5477 time_t interval
= chanserv_conf
.channel_expire_delay
;
5478 char buffer
[INTERVALLEN
];
5479 unsigned int limit
= 25, matches
= 0;
5483 interval
= ParseInterval(argv
[1]);
5485 limit
= atoi(argv
[2]);
5488 intervalString(buffer
, interval
, user
->handle_info
);
5489 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5491 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5493 if((now
- cData
->visited
) < interval
)
5496 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5497 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5504 static MODCMD_FUNC(chan_opt_defaulttopic
)
5510 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5512 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5516 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5518 free(channel
->channel_info
->topic
);
5519 if(topic
[0] == '*' && topic
[1] == 0)
5521 topic
= channel
->channel_info
->topic
= NULL
;
5525 topic
= channel
->channel_info
->topic
= strdup(topic
);
5526 if(channel
->channel_info
->topic_mask
5527 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5528 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5530 SetChannelTopic(channel
, chanserv
, chanserv
, topic
? topic
: "", 1);
5533 if(channel
->channel_info
->topic
)
5534 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5536 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5540 static MODCMD_FUNC(chan_opt_topicmask
)
5544 struct chanData
*cData
= channel
->channel_info
;
5547 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5549 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5553 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5555 if(cData
->topic_mask
)
5556 free(cData
->topic_mask
);
5557 if(mask
[0] == '*' && mask
[1] == 0)
5559 cData
->topic_mask
= 0;
5563 cData
->topic_mask
= strdup(mask
);
5565 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5566 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5567 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5571 if(channel
->channel_info
->topic_mask
)
5572 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5574 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5578 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5582 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5586 if(greeting
[0] == '*' && greeting
[1] == 0)
5590 unsigned int length
= strlen(greeting
);
5591 if(length
> chanserv_conf
.greeting_length
)
5593 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5596 *data
= strdup(greeting
);
5605 reply(name
, user_find_message(user
, "MSG_NONE"));
5609 static MODCMD_FUNC(chan_opt_greeting
)
5611 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5614 static MODCMD_FUNC(chan_opt_usergreeting
)
5616 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5619 static MODCMD_FUNC(chan_opt_modes
)
5621 struct mod_chanmode
*new_modes
;
5622 char modes
[MODELEN
];
5626 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5628 reply("CSMSG_NO_ACCESS");
5631 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5633 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5635 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1,MCP_KEY_FREE
|MCP_REGISTERED
, 0)))
5637 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5640 else if(new_modes
->argc
> 1)
5642 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5643 mod_chanmode_free(new_modes
);
5648 channel
->channel_info
->modes
= *new_modes
;
5649 modcmd_chanmode_announce(new_modes
);
5650 mod_chanmode_free(new_modes
);
5654 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5656 reply("CSMSG_SET_MODES", modes
);
5658 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5662 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5664 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5666 struct chanData
*cData
= channel
->channel_info
;
5671 /* Set flag according to value. */
5672 if(enabled_string(argv
[1]))
5674 cData
->flags
|= mask
;
5677 else if(disabled_string(argv
[1]))
5679 cData
->flags
&= ~mask
;
5684 reply("MSG_INVALID_BINARY", argv
[1]);
5690 /* Find current option value. */
5691 value
= (cData
->flags
& mask
) ? 1 : 0;
5695 reply(name
, user_find_message(user
, "MSG_ON"));
5697 reply(name
, user_find_message(user
, "MSG_OFF"));
5701 static MODCMD_FUNC(chan_opt_nodelete
)
5703 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5705 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5709 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5712 static MODCMD_FUNC(chan_opt_dynlimit
)
5714 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5717 static MODCMD_FUNC(chan_opt_offchannel
)
5719 struct chanData
*cData
= channel
->channel_info
;
5724 /* Set flag according to value. */
5725 if(enabled_string(argv
[1]))
5727 if(!IsOffChannel(cData
))
5728 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5729 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5732 else if(disabled_string(argv
[1]))
5734 if(IsOffChannel(cData
))
5736 struct mod_chanmode change
;
5737 mod_chanmode_init(&change
);
5739 change
.args
[0].mode
= MODE_CHANOP
;
5740 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5741 mod_chanmode_announce(chanserv
, channel
, &change
);
5743 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5748 reply("MSG_INVALID_BINARY", argv
[1]);
5754 /* Find current option value. */
5755 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5759 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5761 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5765 static MODCMD_FUNC(chan_opt_defaults
)
5767 struct userData
*uData
;
5768 struct chanData
*cData
;
5769 const char *confirm
;
5770 enum levelOption lvlOpt
;
5771 enum charOption chOpt
;
5773 cData
= channel
->channel_info
;
5774 uData
= GetChannelUser(cData
, user
->handle_info
);
5775 if(!uData
|| (uData
->access
< UL_OWNER
))
5777 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5780 confirm
= make_confirmation_string(uData
);
5781 if((argc
< 2) || strcmp(argv
[1], confirm
))
5783 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5786 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5787 cData
->modes
= chanserv_conf
.default_modes
;
5788 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5789 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5790 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5791 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5792 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5797 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5799 struct chanData
*cData
= channel
->channel_info
;
5800 struct userData
*uData
;
5801 unsigned short value
;
5805 if(!check_user_level(channel
, user
, option
, 1, 1))
5807 reply("CSMSG_CANNOT_SET");
5810 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5811 if(!value
&& strcmp(argv
[1], "0"))
5813 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5816 uData
= GetChannelUser(cData
, user
->handle_info
);
5817 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5819 reply("CSMSG_BAD_SETLEVEL");
5825 /* This test only applies to owners, since non-owners
5826 * trying to set an option to above their level get caught
5827 * by the CSMSG_BAD_SETLEVEL test above.
5829 if(value
> uData
->access
)
5831 reply("CSMSG_BAD_SETTERS");
5838 cData
->lvlOpts
[option
] = value
;
5840 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5844 static MODCMD_FUNC(chan_opt_enfops
)
5846 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5849 static MODCMD_FUNC(chan_opt_enfhalfops
)
5851 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5853 static MODCMD_FUNC(chan_opt_enfmodes
)
5855 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5858 static MODCMD_FUNC(chan_opt_enftopic
)
5860 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5863 static MODCMD_FUNC(chan_opt_pubcmd
)
5865 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5868 static MODCMD_FUNC(chan_opt_setters
)
5870 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5873 static MODCMD_FUNC(chan_opt_userinfo
)
5875 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5878 static MODCMD_FUNC(chan_opt_topicsnarf
)
5880 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5883 static MODCMD_FUNC(chan_opt_inviteme
)
5885 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5888 /* TODO: Make look like this when no args are
5890 * -X3- -------------------------------
5891 * -X3- BanTimeout: Bans are removed:
5892 * -X3- ----- * indicates current -----
5893 * -X3- 0: [*] Never.
5894 * -X3- 1: [ ] After 10 minutes.
5895 * -X3- 2: [ ] After 2 hours.
5896 * -X3- 3: [ ] After 4 hours.
5897 * -X3- 4: [ ] After 24 hours.
5898 * -X3- 5: [ ] After one week.
5899 * -X3- ------------- End -------------
5902 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5904 struct chanData
*cData
= channel
->channel_info
;
5905 int count
= charOptions
[option
].count
, index
;
5909 index
= atoi(argv
[1]);
5911 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5913 reply("CSMSG_INVALID_NUMERIC", index
);
5914 /* Show possible values. */
5915 for(index
= 0; index
< count
; index
++)
5916 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5920 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5924 /* Find current option value. */
5927 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5931 /* Somehow, the option value is corrupt; reset it to the default. */
5932 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5937 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5941 static MODCMD_FUNC(chan_opt_automode
)
5943 return channel_multiple_option(chAutomode
, CSFUNC_ARGS
);
5946 static MODCMD_FUNC(chan_opt_protect
)
5948 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5951 static MODCMD_FUNC(chan_opt_toys
)
5953 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5956 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5958 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5961 static MODCMD_FUNC(chan_opt_bantimeout
)
5963 return channel_multiple_option(chBanTimeout
, CSFUNC_ARGS
);
5966 static MODCMD_FUNC(chan_opt_topicrefresh
)
5968 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5971 static struct svccmd_list set_shows_list
;
5974 handle_svccmd_unbind(struct svccmd
*target
) {
5976 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5977 if(target
== set_shows_list
.list
[ii
])
5978 set_shows_list
.used
= 0;
5981 static CHANSERV_FUNC(cmd_set
)
5983 struct svccmd
*subcmd
;
5987 /* Check if we need to (re-)initialize set_shows_list. */
5988 if(!set_shows_list
.used
)
5990 if(!set_shows_list
.size
)
5992 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5993 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5995 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5997 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5998 sprintf(buf
, "%s %s", argv
[0], name
);
5999 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6002 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
6005 svccmd_list_append(&set_shows_list
, subcmd
);
6011 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
6012 if(user
->handle_info
&& user
->handle_info
->userlist_style
!= HI_STYLE_CLEAN
)
6014 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
6016 subcmd
= set_shows_list
.list
[ii
];
6017 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
6019 reply("CSMSG_CHANNEL_OPTIONS_END");
6023 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6024 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6027 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6030 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
6032 reply("CSMSG_NO_ACCESS");
6036 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6040 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
6042 struct userData
*uData
;
6044 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6047 reply("CSMSG_NOT_USER", channel
->name
);
6053 /* Just show current option value. */
6055 else if(enabled_string(argv
[1]))
6057 uData
->flags
|= mask
;
6059 else if(disabled_string(argv
[1]))
6061 uData
->flags
&= ~mask
;
6065 reply("MSG_INVALID_BINARY", argv
[1]);
6069 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
6073 static MODCMD_FUNC(user_opt_autoop
)
6075 struct userData
*uData
;
6077 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6080 reply("CSMSG_NOT_USER", channel
->name
);
6083 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6084 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
6086 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
6087 /* TODO: add halfops error message? or is the op one generic enough? */
6090 static MODCMD_FUNC(user_opt_autoinvite
)
6092 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
6095 static MODCMD_FUNC(user_opt_info
)
6097 struct userData
*uData
;
6100 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
6104 /* If they got past the command restrictions (which require access)
6105 * but fail this test, we have some fool with security override on.
6107 reply("CSMSG_NOT_USER", channel
->name
);
6114 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
6115 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
6117 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
6120 bp
= strcspn(infoline
, "\001");
6123 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
6128 if(infoline
[0] == '*' && infoline
[1] == 0)
6131 uData
->info
= strdup(infoline
);
6134 reply("CSMSG_USET_INFO", uData
->info
);
6136 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
6140 struct svccmd_list uset_shows_list
;
6142 static CHANSERV_FUNC(cmd_uset
)
6144 struct svccmd
*subcmd
;
6148 /* Check if we need to (re-)initialize uset_shows_list. */
6149 if(!uset_shows_list
.used
)
6153 "AutoOp", "AutoInvite", "Info"
6156 if(!uset_shows_list
.size
)
6158 uset_shows_list
.size
= ArrayLength(options
);
6159 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
6161 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
6163 const char *name
= options
[ii
];
6164 sprintf(buf
, "%s %s", argv
[0], name
);
6165 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6168 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
6171 svccmd_list_append(&uset_shows_list
, subcmd
);
6177 /* Do this so options are presented in a consistent order. */
6178 reply("CSMSG_USER_OPTIONS");
6179 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
6180 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
6184 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
6185 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
6188 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
6192 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
6195 static CHANSERV_FUNC(cmd_giveownership
)
6197 struct handle_info
*new_owner_hi
;
6198 struct userData
*new_owner
, *curr_user
;
6199 struct chanData
*cData
= channel
->channel_info
;
6200 struct do_not_register
*dnr
;
6201 struct giveownership
*giveownership
;
6202 unsigned int force
, override
;
6203 unsigned short co_access
, new_owner_old_access
;
6204 char reason
[MAXLEN
], transfer_reason
[MAXLEN
];
6207 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
6208 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
6210 struct userData
*uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 1, 0);
6211 override
= ((cmd
->effective_flags
& MODCMD_REQUIRE_CHANUSER
)
6212 && (uData
->access
> 500)
6213 && (!(uData
= _GetChannelUser(channel
->channel_info
, user
->handle_info
, 0, 0))
6214 || uData
->access
< 500));
6217 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
6219 struct userData
*owner
= NULL
;
6220 for(curr_user
= channel
->channel_info
->users
;
6222 curr_user
= curr_user
->next
)
6224 if(curr_user
->access
!= UL_OWNER
)
6228 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
6235 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
6237 char delay
[INTERVALLEN
];
6238 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
6239 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
6242 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
6244 if(new_owner_hi
== user
->handle_info
)
6246 reply("CSMSG_NO_TRANSFER_SELF");
6249 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
6254 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
6258 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
6262 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
6264 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
6267 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
6268 if(!IsHelping(user
))
6269 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
6271 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
6275 new_owner_old_access
= new_owner
->access
;
6276 if(new_owner
->access
>= UL_COOWNER
)
6277 co_access
= new_owner
->access
;
6279 co_access
= UL_COOWNER
;
6280 new_owner
->access
= UL_OWNER
;
6282 curr_user
->access
= co_access
;
6283 cData
->ownerTransfer
= now
;
6285 giveownership
= calloc(1, sizeof(*giveownership
));
6286 giveownership
->issued
= now
;
6287 giveownership
->old_owner
= curr_user
->handle
->handle
;
6288 giveownership
->target
= new_owner_hi
->handle
;
6289 giveownership
->target_access
= new_owner_old_access
;
6292 if(argc
> (2 + force
))
6294 unsplit_string(argv
+ 2 + force
, argc
- 2 - force
, transfer_reason
);
6295 giveownership
->reason
= strdup(transfer_reason
);
6297 giveownership
->staff_issuer
= strdup(user
->handle_info
->handle
);
6300 giveownership
->previous
= channel
->channel_info
->giveownership
;
6301 channel
->channel_info
->giveownership
= giveownership
;
6303 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
6304 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
6305 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
6310 chanserv_expire_user_suspension(void *data
)
6312 struct userData
*target
= data
;
6314 target
->expires
= 0;
6315 target
->flags
&= ~USER_SUSPENDED
;
6318 static CHANSERV_FUNC(cmd_suspend
)
6320 struct handle_info
*hi
;
6321 struct userData
*self
, *target
;
6325 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6326 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6327 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6329 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6332 if(target
->access
>= self
->access
)
6334 reply("MSG_USER_OUTRANKED", hi
->handle
);
6337 if(target
->flags
& USER_SUSPENDED
)
6339 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
6344 target
->present
= 0;
6347 if(!strcmp(argv
[2], "0"))
6351 unsigned int duration
;
6352 if(!(duration
= ParseInterval(argv
[2])))
6354 reply("MSG_INVALID_DURATION", argv
[2]);
6357 expiry
= now
+ duration
;
6360 target
->expires
= expiry
;
6363 timeq_add(target
->expires
, chanserv_expire_user_suspension
, target
);
6365 target
->flags
|= USER_SUSPENDED
;
6366 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6370 static CHANSERV_FUNC(cmd_unsuspend
)
6372 struct handle_info
*hi
;
6373 struct userData
*self
, *target
;
6376 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6377 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6378 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6380 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6383 if(target
->access
>= self
->access
)
6385 reply("MSG_USER_OUTRANKED", hi
->handle
);
6388 if(!(target
->flags
& USER_SUSPENDED
))
6390 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6393 target
->flags
&= ~USER_SUSPENDED
;
6394 scan_user_presence(target
, NULL
);
6395 timeq_del(target
->expires
, chanserv_expire_user_suspension
, target
, 0);
6396 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6400 static MODCMD_FUNC(cmd_deleteme
)
6402 struct handle_info
*hi
;
6403 struct userData
*target
;
6404 const char *confirm_string
;
6405 unsigned short access
;
6408 hi
= user
->handle_info
;
6409 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6411 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6414 if(target
->access
== UL_OWNER
)
6416 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6419 confirm_string
= make_confirmation_string(target
);
6420 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6422 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6425 access
= target
->access
;
6426 channel_name
= strdup(channel
->name
);
6427 del_channel_user(target
, 1);
6428 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6434 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6436 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6437 struct chanData
*cData
;
6440 for(cData
= channelList
; cData
; cData
= cData
->next
)
6442 if(IsSuspended(cData
))
6444 opt
= cData
->chOpts
[chTopicRefresh
];
6447 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6450 SetChannelTopic(cData
->channel
, chanserv
, chanserv
, cData
->topic
, 1);
6451 cData
->last_refresh
= refresh_num
;
6453 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6456 static CHANSERV_FUNC(cmd_unf
)
6460 char response
[MAXLEN
];
6461 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6462 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6463 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6466 reply("CSMSG_UNF_RESPONSE");
6470 static CHANSERV_FUNC(cmd_ping
)
6474 char response
[MAXLEN
];
6475 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6476 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6477 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6480 reply("CSMSG_PING_RESPONSE");
6484 static CHANSERV_FUNC(cmd_wut
)
6488 char response
[MAXLEN
];
6489 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6490 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6491 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6494 reply("CSMSG_WUT_RESPONSE");
6499 static CHANSERV_FUNC(cmd_8ball
)
6501 unsigned int i
, j
, accum
;
6506 for(i
=1; i
<argc
; i
++)
6507 for(j
=0; argv
[i
][j
]; j
++)
6508 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6509 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6512 char response
[MAXLEN
];
6513 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6514 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6517 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6521 #else /* Use cool 8ball instead */
6523 void eightball(char *outcome
, int method
, unsigned int seed
)
6527 #define NUMOFCOLORS 18
6528 char ballcolors
[50][50] = {"blue", "red", "green", "yellow",
6529 "white", "black", "grey", "brown",
6530 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6531 "fuchsia","turquoise","magenta", "cyan"};
6532 #define NUMOFLOCATIONS 50
6533 char balllocations
[50][55] = {
6534 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6535 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6536 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6537 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6538 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6539 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6540 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6541 "your bra", "your hair", "your bed", "the couch", "the wall",
6542 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6543 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6544 #define NUMOFPREPS 15
6545 char ballpreps
[50][50] = {
6546 "Near", "Somewhere near", "In", "In", "In",
6547 "In", "Hiding in", "Under", "Next to", "Over",
6548 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6549 #define NUMOFNUMS 34
6550 char ballnums
[50][50] = {
6551 "A hundred", "A thousand", "A few", "42",
6552 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6553 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6554 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6556 #define NUMOFMULTS 8
6557 char ballmults
[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6560 * 0: normal (Not used in x3)
6567 if (method
== 1) /* A Color */
6571 answer
= (rand() % 12); /* Make sure this is the # of entries */
6574 case 0: strcpy(tmp
, "Very bright %s, I'd say.");
6576 case 1: strcpy(tmp
, "Sort of a light %s color.");
6578 case 2: strcpy(tmp
, "Dark and dreary %s.");
6580 case 3: strcpy(tmp
, "Quite a pale shade of %s.");
6582 case 4: strcpy(tmp
, "A gross kind of mucky %s.");
6584 case 5: strcpy(tmp
, "Brilliant whiteish %s.");
6586 case 6: case 7: case 8: case 9: strcpy(tmp
, "%s.");
6588 case 10: strcpy(tmp
, "Solid %s.");
6590 case 11: strcpy(tmp
, "Transparent %s.");
6592 default: strcpy(outcome
, "An invalid random number was generated.");
6595 sprintf(outcome
, tmp
, ballcolors
[rand() % NUMOFCOLORS
]);
6598 else if (method
== 2) /* Location */
6600 sprintf(outcome
, "%s %s.", ballpreps
[rand() % NUMOFPREPS
], balllocations
[rand() % NUMOFLOCATIONS
]);
6602 else if (method
== 3) /* Number of ___ */
6604 sprintf(outcome
, "%s%s.", ballnums
[rand() % NUMOFNUMS
], ballmults
[rand() % NUMOFMULTS
]);
6608 //Debug(DBGWARNING, "Error in 8ball.");
6613 static CHANSERV_FUNC(cmd_8ball
)
6615 char *word1
, *word2
, *word3
;
6616 static char eb
[MAXLEN
];
6617 unsigned int accum
, i
, j
;
6621 for(i
=1; i
<argc
; i
++)
6622 for(j
=0; argv
[i
][j
]; j
++)
6623 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6625 accum
+= time(NULL
)/3600;
6627 word2
= argc
>2?argv
[2]:"";
6628 word3
= argc
>3?argv
[3]:"";
6631 if((word2
) && strcasecmp(word1
, "what") == 0 && strcasecmp(word2
, "color") == 0)
6632 eightball(eb
, 1, accum
);
6633 else if((word3
) && strcasecmp(word1
, "what's") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6634 eightball(eb
, 1, accum
);
6635 else if((word3
) && strcasecmp(word1
, "whats") == 0 && strcasecmp(word2
, "the") == 0 && strcasecmp(word3
, "color") == 0)
6636 eightball(eb
, 1, accum
);
6637 /*** LOCATION *****/
6642 (strcasecmp(word1
, "where") == 0) &&
6643 (strcasecmp(word2
, "is") == 0)
6647 strcasecmp(word1
, "where's") == 0
6650 eightball(eb
, 2, accum
);
6652 else if((word2
) && strcasecmp(word1
, "how") == 0 && strcasecmp(word2
, "many") == 0)
6653 eightball(eb
, 3, accum
);
6657 /* Generic 8ball question.. so pull from x3.conf srvx style */
6660 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6663 char response
[MAXLEN
];
6664 sprintf(response
, "\002%s\002: %s", user
->nick
, resp
);
6665 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6668 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6674 char response
[MAXLEN
];
6675 sprintf(response
, "\002%s\002: %s", user
->nick
, eb
);
6676 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6679 send_message_type(4, user
, cmd
->parent
->bot
, "%s", eb
);
6684 static CHANSERV_FUNC(cmd_d
)
6686 unsigned long sides
, count
, modifier
, ii
, total
;
6687 char response
[MAXLEN
], *sep
;
6691 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6701 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6702 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6706 else if((sep
[0] == '-') && isdigit(sep
[1]))
6707 modifier
= strtoul(sep
, NULL
, 10);
6708 else if((sep
[0] == '+') && isdigit(sep
[1]))
6709 modifier
= strtoul(sep
+1, NULL
, 10);
6716 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6721 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6724 for(total
= ii
= 0; ii
< count
; ++ii
)
6725 total
+= (rand() % sides
) + 1;
6728 if((count
> 1) || modifier
)
6730 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6731 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6735 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6736 sprintf(response
, fmt
, total
, sides
);
6739 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6741 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6745 static CHANSERV_FUNC(cmd_huggle
)
6747 /* CTCP must be via PRIVMSG, never notice */
6749 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6751 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6755 static CHANSERV_FUNC(cmd_calc
)
6757 char response
[MAXLEN
];
6760 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6763 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6765 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6770 chanserv_adjust_limit(void *data
)
6772 struct mod_chanmode change
;
6773 struct chanData
*cData
= data
;
6774 struct chanNode
*channel
= cData
->channel
;
6777 if(IsSuspended(cData
))
6780 cData
->limitAdjusted
= now
;
6781 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6782 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6784 if(limit
> cData
->modes
.new_limit
)
6785 limit
= cData
->modes
.new_limit
;
6786 else if(limit
== cData
->modes
.new_limit
)
6790 mod_chanmode_init(&change
);
6791 change
.modes_set
= MODE_LIMIT
;
6792 change
.new_limit
= limit
;
6793 mod_chanmode_announce(chanserv
, channel
, &change
);
6797 handle_new_channel(struct chanNode
*channel
)
6799 struct chanData
*cData
;
6801 if(!(cData
= channel
->channel_info
))
6804 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6805 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6807 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6808 SetChannelTopic(channel
, chanserv
, chanserv
, channel
->channel_info
->topic
, 1);
6811 /* Welcome to my worst nightmare. Warning: Read (or modify)
6812 the code below at your own risk. */
6814 handle_join(struct modeNode
*mNode
)
6816 struct mod_chanmode change
;
6817 struct userNode
*user
= mNode
->user
;
6818 struct chanNode
*channel
= mNode
->channel
;
6819 struct chanData
*cData
;
6820 struct userData
*uData
= NULL
;
6821 struct banData
*bData
;
6822 struct handle_info
*handle
;
6823 unsigned int modes
= 0, info
= 0;
6826 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6829 cData
= channel
->channel_info
;
6830 if(channel
->members
.used
> cData
->max
)
6831 cData
->max
= channel
->members
.used
;
6834 /* Check for bans. If they're joining through a ban, one of two
6836 * 1: Join during a netburst, by riding the break. Kick them
6837 * unless they have ops or voice in the channel.
6838 * 2: They're allowed to join through the ban (an invite in
6839 * ircu2.10, or a +e on Hybrid, or something).
6840 * If they're not joining through a ban, and the banlist is not
6841 * full, see if they're on the banlist for the channel. If so,
6844 if(user
->uplink
->burst
&& !mNode
->modes
)
6847 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
6849 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
6851 /* Riding a netburst. Naughty. */
6852 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
6859 mod_chanmode_init(&change
);
6861 if(channel
->banlist
.used
< MAXBANS
)
6863 /* Not joining through a ban. */
6864 for(bData
= cData
->bans
;
6865 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
6866 bData
= bData
->next
);
6870 char kick_reason
[MAXLEN
];
6871 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6873 bData
->triggered
= now
;
6874 if(bData
!= cData
->bans
)
6876 /* Shuffle the ban to the head of the list. */
6878 bData
->next
->prev
= bData
->prev
;
6880 bData
->prev
->next
= bData
->next
;
6883 bData
->next
= cData
->bans
;
6886 cData
->bans
->prev
= bData
;
6887 cData
->bans
= bData
;
6890 change
.args
[0].mode
= MODE_BAN
;
6891 change
.args
[0].u
.hostmask
= bData
->mask
;
6892 mod_chanmode_announce(chanserv
, channel
, &change
);
6893 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6898 /* ChanServ will not modify the limits in join-flooded channels.
6899 It will also skip DynLimit processing when the user (or srvx)
6900 is bursting in, because there are likely more incoming. */
6901 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6902 && !user
->uplink
->burst
6903 && !channel
->join_flooded
6904 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6906 /* The user count has begun "bumping" into the channel limit,
6907 so set a timer to raise the limit a bit. Any previous
6908 timers are removed so three incoming users within the delay
6909 results in one limit change, not three. */
6911 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6912 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6915 /* Give automodes exept during join-floods */
6916 if(!channel
->join_flooded
)
6918 if(cData
->chOpts
[chAutomode
] == 'v')
6919 modes
|= MODE_VOICE
;
6920 else if(cData
->chOpts
[chAutomode
] == 'h')
6921 modes
|= MODE_HALFOP
;
6922 else if(cData
->chOpts
[chAutomode
] == 'o')
6923 modes
|= MODE_CHANOP
;
6926 greeting
= cData
->greeting
;
6927 if(user
->handle_info
)
6929 handle
= user
->handle_info
;
6931 if(IsHelper(user
) && !IsHelping(user
))
6934 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6936 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6938 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6944 uData
= GetTrueChannelAccess(cData
, handle
);
6945 if(uData
&& !IsUserSuspended(uData
))
6947 /* non users getting automodes are handled above. */
6948 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
6950 if(uData
->access
>= UL_OP
)
6951 modes
|= MODE_CHANOP
;
6952 else if(uData
->access
>= UL_HALFOP
)
6953 modes
|= MODE_HALFOP
;
6954 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
6955 modes
|= MODE_VOICE
;
6957 if(uData
->access
>= UL_PRESENT
)
6958 cData
->visited
= now
;
6959 if(cData
->user_greeting
)
6960 greeting
= cData
->user_greeting
;
6962 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6963 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6971 /* If user joining normally (not during burst), apply op or voice,
6972 * and send greeting/userinfo as appropriate.
6974 if(!user
->uplink
->burst
)
6978 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
6979 if(modes & MODE_CHANOP) {
6980 modes &= ~MODE_HALFOP;
6981 modes &= ~MODE_VOICE;
6984 change
.args
[0].mode
= modes
;
6985 change
.args
[0].u
.member
= mNode
;
6986 mod_chanmode_announce(chanserv
, channel
, &change
);
6989 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6991 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6997 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6999 struct mod_chanmode change
;
7000 struct userData
*channel
;
7001 unsigned int ii
, jj
;
7003 if(!user
->handle_info
)
7006 mod_chanmode_init(&change
);
7008 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7010 struct chanNode
*cn
;
7011 struct modeNode
*mn
;
7012 if(IsUserSuspended(channel
)
7013 || IsSuspended(channel
->channel
)
7014 || !(cn
= channel
->channel
->channel
))
7017 mn
= GetUserMode(cn
, user
);
7020 if(!IsUserSuspended(channel
)
7021 && IsUserAutoInvite(channel
)
7022 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7024 && !user
->uplink
->burst
)
7025 irc_invite(chanserv
, user
, cn
);
7029 if(channel
->access
>= UL_PRESENT
)
7030 channel
->channel
->visited
= now
;
7032 if(IsUserAutoOp(channel
))
7034 if(channel
->access
>= UL_OP
)
7035 change
.args
[0].mode
= MODE_CHANOP
;
7036 else if(channel
->access
>= UL_HALFOP
)
7037 change
.args
[0].mode
= MODE_HALFOP
;
7038 else if(channel
->access
>= UL_PEON
)
7039 change
.args
[0].mode
= MODE_VOICE
;
7041 change
.args
[0].mode
= 0;
7042 change
.args
[0].u
.member
= mn
;
7043 if(change
.args
[0].mode
)
7044 mod_chanmode_announce(chanserv
, cn
, &change
);
7047 channel
->seen
= now
;
7048 channel
->present
= 1;
7051 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7053 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7054 struct banData
*ban
;
7056 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7057 || !channel
->channel_info
7058 || IsSuspended(channel
->channel_info
))
7060 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7061 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7063 if(jj
< channel
->banlist
.used
)
7065 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7067 char kick_reason
[MAXLEN
];
7068 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7070 change
.args
[0].mode
= MODE_BAN
;
7071 change
.args
[0].u
.hostmask
= ban
->mask
;
7072 mod_chanmode_announce(chanserv
, channel
, &change
);
7073 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7074 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7075 ban
->triggered
= now
;
7080 if(IsSupportHelper(user
))
7082 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7084 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7086 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7094 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7096 struct chanData
*cData
;
7097 struct userData
*uData
;
7099 cData
= mn
->channel
->channel_info
;
7100 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7103 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7105 /* Allow for a bit of padding so that the limit doesn't
7106 track the user count exactly, which could get annoying. */
7107 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7109 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7110 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7114 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7116 scan_user_presence(uData
, mn
->user
);
7120 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7122 unsigned int ii
, jj
;
7123 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7125 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7126 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7128 if(jj
< mn
->user
->channels
.used
)
7131 if(ii
== chanserv_conf
.support_channels
.used
)
7132 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7137 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7139 struct userData
*uData
;
7141 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7142 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7143 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7146 if(protect_user(victim
, kicker
, channel
->channel_info
))
7148 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7149 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7152 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7157 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7159 struct chanData
*cData
;
7161 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7164 cData
= channel
->channel_info
;
7165 if(bad_topic(channel
, user
, channel
->topic
))
7166 { /* User doesnt have privs to set topics. Undo it */
7167 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7168 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7171 /* If there is a topic mask set, and the new topic doesnt match,
7172 * set the topic to mask + new_topic */
7173 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7175 char new_topic
[TOPICLEN
+1];
7176 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7179 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7180 /* and fall through to topicsnarf code below.. */
7182 else /* Topic couldnt fit into mask, was too long */
7184 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7185 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7186 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7190 /* With topicsnarf, grab the topic and save it as the default topic. */
7191 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7194 cData
->topic
= strdup(channel
->topic
);
7200 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7202 struct mod_chanmode
*bounce
= NULL
;
7203 unsigned int bnc
, ii
;
7206 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7209 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7210 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7212 char correct
[MAXLEN
];
7213 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7214 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7215 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7217 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7219 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7221 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7222 if(!protect_user(victim
, user
, channel
->channel_info
))
7225 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7228 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7229 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7230 if(bounce
->args
[bnc
].u
.member
)
7234 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7235 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7237 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7239 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7241 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7242 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7245 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7246 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7247 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7250 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7252 const char *ban
= change
->args
[ii
].u
.hostmask
;
7253 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7256 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7257 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7258 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7260 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7265 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7266 mod_chanmode_announce(chanserv
, channel
, bounce
);
7267 for(ii
= 0; ii
< change
->argc
; ++ii
)
7268 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7269 free((char*)bounce
->args
[ii
].u
.hostmask
);
7270 mod_chanmode_free(bounce
);
7275 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7277 struct chanNode
*channel
;
7278 struct banData
*bData
;
7279 struct mod_chanmode change
;
7280 unsigned int ii
, jj
;
7281 char kick_reason
[MAXLEN
];
7283 mod_chanmode_init(&change
);
7285 change
.args
[0].mode
= MODE_BAN
;
7286 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7288 channel
= user
->channels
.list
[ii
]->channel
;
7289 /* Need not check for bans if they're opped or voiced. */
7290 /* TODO: does this make sense in automode v, h, and o? *
7291 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7292 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7294 /* Need not check for bans unless channel registration is active. */
7295 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7297 /* Look for a matching ban already on the channel. */
7298 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7299 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7301 /* Need not act if we found one. */
7302 if(jj
< channel
->banlist
.used
)
7304 /* Look for a matching ban in this channel. */
7305 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7307 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7309 change
.args
[0].u
.hostmask
= bData
->mask
;
7310 mod_chanmode_announce(chanserv
, channel
, &change
);
7311 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7312 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7313 bData
->triggered
= now
;
7314 break; /* we don't need to check any more bans in the channel */
7319 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7321 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7325 dict_remove2(handle_dnrs
, old_handle
, 1);
7326 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7327 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7332 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7334 struct userNode
*h_user
;
7336 if(handle
->channels
)
7338 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7339 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7341 while(handle
->channels
)
7342 del_channel_user(handle
->channels
, 1);
7347 handle_server_link(UNUSED_ARG(struct server
*server
))
7349 struct chanData
*cData
;
7351 for(cData
= channelList
; cData
; cData
= cData
->next
)
7353 if(!IsSuspended(cData
))
7354 cData
->may_opchan
= 1;
7355 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7356 && !cData
->channel
->join_flooded
7357 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7358 < chanserv_conf
.adjust_threshold
))
7360 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7361 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7367 chanserv_conf_read(void)
7371 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7372 struct mod_chanmode
*change
;
7373 struct string_list
*strlist
;
7374 struct chanNode
*chan
;
7377 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7379 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7382 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7383 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7384 chanserv_conf
.support_channels
.used
= 0;
7385 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7387 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7389 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7392 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7394 channelList_append(&chanserv_conf
.support_channels
, chan
);
7397 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7400 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7403 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7405 channelList_append(&chanserv_conf
.support_channels
, chan
);
7407 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7408 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7409 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7410 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7411 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7412 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7413 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7414 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7415 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7416 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7417 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7418 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7419 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7420 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7421 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7422 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7423 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7424 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7425 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7426 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7427 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7428 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7429 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7430 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7431 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7433 NickChange(chanserv
, str
, 0);
7434 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7435 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7436 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7437 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7438 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7439 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7440 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7441 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7442 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7443 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7444 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7445 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7446 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7447 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7448 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7449 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7450 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7453 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7454 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7455 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7456 && (change
->argc
< 2))
7458 chanserv_conf
.default_modes
= *change
;
7459 mod_chanmode_free(change
);
7461 free_string_list(chanserv_conf
.set_shows
);
7462 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7464 strlist
= string_list_copy(strlist
);
7467 static const char *list
[] = {
7468 /* free form text */
7469 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7470 /* options based on user level */
7471 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7472 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7473 /* multiple choice options */
7474 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7475 /* binary options */
7476 "DynLimit", "NoDelete", "BanTimeout",
7481 strlist
= alloc_string_list(ArrayLength(list
)-1);
7482 for(ii
=0; list
[ii
]; ii
++)
7483 string_list_append(strlist
, strdup(list
[ii
]));
7485 chanserv_conf
.set_shows
= strlist
;
7486 /* We don't look things up now, in case the list refers to options
7487 * defined by modules initialized after this point. Just mark the
7488 * function list as invalid, so it will be initialized.
7490 set_shows_list
.used
= 0;
7491 free_string_list(chanserv_conf
.eightball
);
7492 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7495 strlist
= string_list_copy(strlist
);
7499 strlist
= alloc_string_list(4);
7500 string_list_append(strlist
, strdup("Yes."));
7501 string_list_append(strlist
, strdup("No."));
7502 string_list_append(strlist
, strdup("Maybe so."));
7504 chanserv_conf
.eightball
= strlist
;
7505 free_string_list(chanserv_conf
.old_ban_names
);
7506 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7508 strlist
= string_list_copy(strlist
);
7510 strlist
= alloc_string_list(2);
7511 chanserv_conf
.old_ban_names
= strlist
;
7512 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7513 off_channel
= str
? atoi(str
) : 0;
7517 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7520 struct note_type
*ntype
;
7523 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7525 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7528 if(!(ntype
= chanserv_create_note_type(key
)))
7530 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7534 /* Figure out set access */
7535 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7537 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7538 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7540 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7542 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7543 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7545 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7547 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7551 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7552 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7553 ntype
->set_access
.min_opserv
= 0;
7556 /* Figure out visibility */
7557 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7558 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7559 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7560 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7561 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7562 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7563 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7564 ntype
->visible_type
= NOTE_VIS_ALL
;
7566 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7568 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7569 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7573 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7575 struct handle_info
*handle
;
7576 struct userData
*uData
;
7577 char *seen
, *inf
, *flags
, *expires
;
7579 unsigned short access
;
7581 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7583 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7587 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7588 if(access
> UL_OWNER
)
7590 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7594 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7595 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7596 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7597 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7598 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7599 handle
= get_handle_info(key
);
7602 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7606 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7607 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7608 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7610 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7612 if(uData
->expires
> now
)
7613 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7615 uData
->flags
&= ~USER_SUSPENDED
;
7618 /* Upgrade: set autoop to the inverse of noautoop */
7619 if(chanserv_read_version
< 2)
7621 /* if noautoop is true, set autoop false, and vice versa */
7622 if(uData
->flags
& USER_NOAUTO_OP
)
7623 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7625 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7626 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
);
7632 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7634 struct banData
*bData
;
7635 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7636 time_t set_time
, triggered_time
, expires_time
;
7638 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7640 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7644 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7645 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7646 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7647 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7648 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7649 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7650 if (!reason
|| !owner
)
7653 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7654 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7656 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7658 expires_time
= set_time
+ atoi(s_duration
);
7662 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7665 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7668 static struct suspended
*
7669 chanserv_read_suspended(dict_t obj
)
7671 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7675 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7676 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7677 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7678 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7679 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7680 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7681 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7682 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7683 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7684 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7688 static struct giveownership
*
7689 chanserv_read_giveownership(dict_t obj
)
7691 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7695 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7696 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7698 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7700 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7701 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7703 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7704 giveownership
->reason
= str
? strdup(str
) : NULL
;
7705 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7706 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7708 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7709 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7710 return giveownership
;
7714 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7716 struct suspended
*suspended
;
7717 struct giveownership
*giveownership
;
7718 struct mod_chanmode
*modes
;
7719 struct chanNode
*cNode
;
7720 struct chanData
*cData
;
7721 struct dict
*channel
, *obj
;
7722 char *str
, *argv
[10];
7726 channel
= hir
->d
.object
;
7728 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7731 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7734 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7737 cData
= register_channel(cNode
, str
);
7740 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7744 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7746 enum levelOption lvlOpt
;
7747 enum charOption chOpt
;
7749 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7750 cData
->flags
= atoi(str
);
7752 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7754 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7756 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7757 else if(levelOptions
[lvlOpt
].old_flag
)
7759 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7760 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7762 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7766 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7768 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7770 cData
->chOpts
[chOpt
] = str
[0];
7773 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7775 enum levelOption lvlOpt
;
7776 enum charOption chOpt
;
7779 cData
->flags
= base64toint(str
, 5);
7780 count
= strlen(str
+= 5);
7781 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7784 if(levelOptions
[lvlOpt
].old_flag
)
7786 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7787 lvl
= levelOptions
[lvlOpt
].flag_value
;
7789 lvl
= levelOptions
[lvlOpt
].default_value
;
7791 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7793 case 'c': lvl
= UL_COOWNER
; break;
7794 case 'm': lvl
= UL_MANAGER
; break;
7795 case 'n': lvl
= UL_OWNER
+1; break;
7796 case 'o': lvl
= UL_OP
; break;
7797 case 'p': lvl
= UL_PEON
; break;
7798 case 'h': lvl
= UL_HALFOP
; break;
7799 case 'w': lvl
= UL_OWNER
; break;
7800 default: lvl
= 0; break;
7802 cData
->lvlOpts
[lvlOpt
] = lvl
;
7804 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7805 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7808 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7810 suspended
= chanserv_read_suspended(obj
);
7811 cData
->suspended
= suspended
;
7812 suspended
->cData
= cData
;
7813 /* We could use suspended->expires and suspended->revoked to
7814 * set the CHANNEL_SUSPENDED flag, but we don't. */
7816 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7818 suspended
= calloc(1, sizeof(*suspended
));
7819 suspended
->issued
= 0;
7820 suspended
->revoked
= 0;
7821 suspended
->suspender
= strdup(str
);
7822 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7823 suspended
->expires
= str
? atoi(str
) : 0;
7824 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7825 suspended
->reason
= strdup(str
? str
: "No reason");
7826 suspended
->previous
= NULL
;
7827 cData
->suspended
= suspended
;
7828 suspended
->cData
= cData
;
7832 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7833 suspended
= NULL
; /* to squelch a warning */
7836 if(IsSuspended(cData
)) {
7837 if(suspended
->expires
> now
)
7838 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7839 else if(suspended
->expires
)
7840 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7843 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
7845 giveownership
= chanserv_read_giveownership(obj
);
7846 cData
->giveownership
= giveownership
;
7849 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7850 struct mod_chanmode change
;
7851 mod_chanmode_init(&change
);
7853 change
.args
[0].mode
= MODE_CHANOP
;
7854 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7855 mod_chanmode_announce(chanserv
, cNode
, &change
);
7858 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7859 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7860 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7861 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7862 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7863 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7864 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7865 cData
->max
= str
? atoi(str
) : 0;
7866 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7867 cData
->greeting
= str
? strdup(str
) : NULL
;
7868 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7869 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7870 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7871 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7872 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7873 cData
->topic
= str
? strdup(str
) : NULL
;
7875 if(!IsSuspended(cData
)
7876 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7877 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7878 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
7879 cData
->modes
= *modes
;
7881 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7882 if(cData
->modes
.argc
> 1)
7883 cData
->modes
.argc
= 1;
7884 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7885 mod_chanmode_free(modes
);
7888 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7889 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7890 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7892 if(!cData
->users
&& !IsProtected(cData
))
7894 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7895 unregister_channel(cData
, "has empty user list.");
7899 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7900 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7901 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7903 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7904 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7906 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7907 struct record_data
*rd
= iter_data(it
);
7908 const char *note
, *setter
;
7910 if(rd
->type
!= RECDB_OBJECT
)
7912 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7916 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7918 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7920 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7924 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7925 if(!setter
) setter
= "<unknown>";
7926 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7934 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7936 const char *setter
, *reason
, *str
;
7937 struct do_not_register
*dnr
;
7939 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7942 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7945 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7948 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7951 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7954 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7956 dnr
->set
= atoi(str
);
7962 chanserv_version_read(struct dict
*section
)
7966 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7968 chanserv_read_version
= atoi(str
);
7969 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7973 chanserv_saxdb_read(struct dict
*database
)
7975 struct dict
*section
;
7978 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7979 chanserv_version_read(section
);
7981 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7982 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7983 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7985 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7986 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7987 chanserv_channel_read(iter_key(it
), iter_data(it
));
7989 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7990 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7991 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7997 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7999 int high_present
= 0;
8000 saxdb_start_record(ctx
, KEY_USERS
, 1);
8001 for(; uData
; uData
= uData
->next
)
8003 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8005 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8006 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8007 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8009 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8011 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8013 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8014 saxdb_end_record(ctx
);
8016 saxdb_end_record(ctx
);
8017 return high_present
;
8021 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8025 saxdb_start_record(ctx
, KEY_BANS
, 1);
8026 for(; bData
; bData
= bData
->next
)
8028 saxdb_start_record(ctx
, bData
->mask
, 0);
8029 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8030 if(bData
->triggered
)
8031 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8033 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8035 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8037 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8038 saxdb_end_record(ctx
);
8040 saxdb_end_record(ctx
);
8044 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8046 saxdb_start_record(ctx
, name
, 0);
8047 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8048 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8050 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8052 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8054 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8056 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8057 saxdb_end_record(ctx
);
8061 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8063 saxdb_start_record(ctx
, name
, 0);
8064 if(giveownership
->staff_issuer
)
8065 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8066 if(giveownership
->old_owner
)
8067 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8068 if(giveownership
->target
)
8069 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8070 if(giveownership
->target_access
)
8071 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8072 if(giveownership
->reason
)
8073 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8074 if(giveownership
->issued
)
8075 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8076 if(giveownership
->previous
)
8077 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8078 saxdb_end_record(ctx
);
8082 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8086 enum levelOption lvlOpt
;
8087 enum charOption chOpt
;
8089 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8091 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8092 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8094 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8095 if(channel
->registrar
)
8096 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8097 if(channel
->greeting
)
8098 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8099 if(channel
->user_greeting
)
8100 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8101 if(channel
->topic_mask
)
8102 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8103 if(channel
->suspended
)
8104 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8105 if(channel
->giveownership
)
8106 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8108 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8109 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8110 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8111 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8112 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8114 buf
[0] = channel
->chOpts
[chOpt
];
8116 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8118 saxdb_end_record(ctx
);
8120 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8122 mod_chanmode_format(&channel
->modes
, buf
);
8123 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8126 high_present
= chanserv_write_users(ctx
, channel
->users
);
8127 chanserv_write_bans(ctx
, channel
->bans
);
8129 if(dict_size(channel
->notes
))
8133 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8134 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8136 struct note
*note
= iter_data(it
);
8137 saxdb_start_record(ctx
, iter_key(it
), 0);
8138 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8139 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8140 saxdb_end_record(ctx
);
8142 saxdb_end_record(ctx
);
8145 if(channel
->ownerTransfer
)
8146 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8147 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8148 saxdb_end_record(ctx
);
8152 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8156 saxdb_start_record(ctx
, ntype
->name
, 0);
8157 switch(ntype
->set_access_type
)
8159 case NOTE_SET_CHANNEL_ACCESS
:
8160 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8162 case NOTE_SET_CHANNEL_SETTER
:
8163 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8165 case NOTE_SET_PRIVILEGED
: default:
8166 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8169 switch(ntype
->visible_type
)
8171 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8172 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8173 case NOTE_VIS_PRIVILEGED
:
8174 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8176 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8177 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8178 saxdb_end_record(ctx
);
8182 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8184 struct do_not_register
*dnr
;
8187 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8189 dnr
= iter_data(it
);
8190 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8192 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8193 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8194 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8195 saxdb_end_record(ctx
);
8200 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8203 struct chanData
*channel
;
8205 /* Version Control*/
8206 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8207 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8208 saxdb_end_record(ctx
);
8211 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8212 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8213 chanserv_write_note_type(ctx
, iter_data(it
));
8214 saxdb_end_record(ctx
);
8217 saxdb_start_record(ctx
, KEY_DNR
, 1);
8218 write_dnrs_helper(ctx
, handle_dnrs
);
8219 write_dnrs_helper(ctx
, plain_dnrs
);
8220 write_dnrs_helper(ctx
, mask_dnrs
);
8221 saxdb_end_record(ctx
);
8224 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8225 for(channel
= channelList
; channel
; channel
= channel
->next
)
8226 chanserv_write_channel(ctx
, channel
);
8227 saxdb_end_record(ctx
);
8233 chanserv_db_cleanup(void) {
8235 unreg_part_func(handle_part
);
8237 unregister_channel(channelList
, "terminating.");
8238 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8239 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8240 free(chanserv_conf
.support_channels
.list
);
8241 dict_delete(handle_dnrs
);
8242 dict_delete(plain_dnrs
);
8243 dict_delete(mask_dnrs
);
8244 dict_delete(note_types
);
8245 free_string_list(chanserv_conf
.eightball
);
8246 free_string_list(chanserv_conf
.old_ban_names
);
8247 free_string_list(chanserv_conf
.set_shows
);
8248 free(set_shows_list
.list
);
8249 free(uset_shows_list
.list
);
8252 struct userData
*helper
= helperList
;
8253 helperList
= helperList
->next
;
8258 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8259 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8260 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8263 init_chanserv(const char *nick
)
8265 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8266 conf_register_reload(chanserv_conf_read
);
8268 reg_server_link_func(handle_server_link
);
8270 reg_new_channel_func(handle_new_channel
);
8271 reg_join_func(handle_join
);
8272 reg_part_func(handle_part
);
8273 reg_kick_func(handle_kick
);
8274 reg_topic_func(handle_topic
);
8275 reg_mode_change_func(handle_mode
);
8276 reg_nick_change_func(handle_nick_change
);
8278 reg_auth_func(handle_auth
);
8279 reg_handle_rename_func(handle_rename
);
8280 reg_unreg_func(handle_unreg
);
8282 handle_dnrs
= dict_new();
8283 dict_set_free_data(handle_dnrs
, free
);
8284 plain_dnrs
= dict_new();
8285 dict_set_free_data(plain_dnrs
, free
);
8286 mask_dnrs
= dict_new();
8287 dict_set_free_data(mask_dnrs
, free
);
8289 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8290 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8291 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8292 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8293 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8294 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8295 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8296 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8297 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8298 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8300 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8302 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8303 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8305 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8306 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8307 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8308 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8309 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8311 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8312 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8313 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8314 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8315 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8316 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8318 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8319 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8320 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8321 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8323 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8324 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8325 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8326 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8327 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8328 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8329 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8330 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8331 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8332 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8334 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8335 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8336 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8337 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8338 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8339 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8340 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8341 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8342 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8343 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8344 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8345 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8346 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8347 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8349 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8350 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8351 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8352 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8354 /* if you change dellamer access, see also places
8355 * like unbanme which have manager hardcoded. */
8356 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8357 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8359 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8361 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8363 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8364 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8365 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8366 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8367 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8368 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8369 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8370 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8371 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8372 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8373 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8374 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8376 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8377 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8379 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8380 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8381 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8382 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8384 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8385 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8386 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8387 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8388 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8390 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8391 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8392 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8393 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8394 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8395 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8396 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8398 /* Channel options */
8399 DEFINE_CHANNEL_OPTION(defaulttopic
);
8400 DEFINE_CHANNEL_OPTION(topicmask
);
8401 DEFINE_CHANNEL_OPTION(greeting
);
8402 DEFINE_CHANNEL_OPTION(usergreeting
);
8403 DEFINE_CHANNEL_OPTION(modes
);
8404 DEFINE_CHANNEL_OPTION(enfops
);
8405 DEFINE_CHANNEL_OPTION(enfhalfops
);
8406 DEFINE_CHANNEL_OPTION(automode
);
8407 DEFINE_CHANNEL_OPTION(protect
);
8408 DEFINE_CHANNEL_OPTION(enfmodes
);
8409 DEFINE_CHANNEL_OPTION(enftopic
);
8410 DEFINE_CHANNEL_OPTION(pubcmd
);
8411 DEFINE_CHANNEL_OPTION(userinfo
);
8412 DEFINE_CHANNEL_OPTION(dynlimit
);
8413 DEFINE_CHANNEL_OPTION(topicsnarf
);
8414 DEFINE_CHANNEL_OPTION(nodelete
);
8415 DEFINE_CHANNEL_OPTION(toys
);
8416 DEFINE_CHANNEL_OPTION(setters
);
8417 DEFINE_CHANNEL_OPTION(topicrefresh
);
8418 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8419 DEFINE_CHANNEL_OPTION(bantimeout
);
8420 DEFINE_CHANNEL_OPTION(inviteme
);
8422 DEFINE_CHANNEL_OPTION(offchannel
);
8423 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8425 /* Alias set topic to set defaulttopic for compatibility. */
8426 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8429 DEFINE_USER_OPTION(autoinvite
);
8430 DEFINE_USER_OPTION(info
);
8431 DEFINE_USER_OPTION(autoop
);
8433 /* Alias uset autovoice to uset autoop. */
8434 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8436 note_types
= dict_new();
8437 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8440 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8441 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8442 service_register(chanserv
)->trigger
= '!';
8443 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8446 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8448 if(chanserv_conf
.channel_expire_frequency
)
8449 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8451 if(chanserv_conf
.ban_timeout_frequency
)
8452 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8454 if(chanserv_conf
.refresh_period
)
8456 time_t next_refresh
;
8457 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8458 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8461 reg_exit_func(chanserv_db_cleanup
);
8462 message_register_table(msgtab
);