1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_VERSION_CONTROL "version_control"
61 #define KEY_CHANNELS "channels"
62 #define KEY_NOTE_TYPES "note_types"
64 /* version control paramiter */
65 #define KEY_VERSION_NUMBER "version_number"
67 /* Note type parameters */
68 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
69 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
70 #define KEY_NOTE_SETTER_ACCESS "setter_access"
71 #define KEY_NOTE_VISIBILITY "visibility"
72 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
73 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
74 #define KEY_NOTE_VIS_ALL "all"
75 #define KEY_NOTE_MAX_LENGTH "max_length"
76 #define KEY_NOTE_SETTER "setter"
77 #define KEY_NOTE_NOTE "note"
79 /* Do-not-register channels */
81 #define KEY_DNR_SET "set"
82 #define KEY_DNR_SETTER "setter"
83 #define KEY_DNR_REASON "reason"
86 #define KEY_REGISTERED "registered"
87 #define KEY_REGISTRAR "registrar"
88 #define KEY_SUSPENDED "suspended"
89 #define KEY_PREVIOUS "previous"
90 #define KEY_SUSPENDER "suspender"
91 #define KEY_ISSUED "issued"
92 #define KEY_REVOKED "revoked"
93 #define KEY_SUSPEND_EXPIRES "suspend_expires"
94 #define KEY_SUSPEND_REASON "suspend_reason"
95 #define KEY_GIVEOWNERSHIP "giveownership"
96 #define KEY_STAFF_ISSUER "staff_issuer"
97 #define KEY_OLD_OWNER "old_owner"
98 #define KEY_TARGET "target"
99 #define KEY_TARGET_ACCESS "target_access"
100 #define KEY_VISITED "visited"
101 #define KEY_TOPIC "topic"
102 #define KEY_GREETING "greeting"
103 #define KEY_USER_GREETING "user_greeting"
104 #define KEY_MODES "modes"
105 #define KEY_FLAGS "flags"
106 #define KEY_OPTIONS "options"
107 #define KEY_USERS "users"
108 #define KEY_BANS "bans" /* for lamers */
109 #define KEY_MAX "max"
110 #define KEY_NOTES "notes"
111 #define KEY_TOPIC_MASK "topic_mask"
112 #define KEY_OWNER_TRANSFER "owner_transfer"
115 #define KEY_LEVEL "level"
116 #define KEY_INFO "info"
117 #define KEY_SEEN "seen"
120 #define KEY_OWNER "owner"
121 #define KEY_REASON "reason"
122 #define KEY_SET "set"
123 #define KEY_DURATION "duration"
124 #define KEY_EXPIRES "expires"
125 #define KEY_TRIGGERED "triggered"
127 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
128 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
130 /* Administrative messages */
131 static const struct message_entry msgtab
[] = {
132 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
134 /* Channel registration */
135 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
136 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
137 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
138 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
139 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
140 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
141 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
142 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
144 /* Do-not-register channels */
145 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
146 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
147 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
148 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
149 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
150 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
151 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
152 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
153 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
154 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
155 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
157 /* Channel unregistration */
158 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
159 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
160 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
161 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
164 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
165 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
167 /* Channel merging */
168 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
169 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
170 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
171 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
172 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
174 /* Handle unregistration */
175 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
178 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
179 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
180 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
181 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
182 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
183 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
184 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
185 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
186 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
187 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
188 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
189 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
190 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
191 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
193 /* Removing yourself from a channel. */
194 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
195 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
196 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
198 /* User management */
199 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
200 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
201 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
202 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
203 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
204 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
205 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
206 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
207 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
208 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
209 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
210 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
211 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
212 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
213 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
215 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
216 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
217 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
218 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
219 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
220 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
223 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
224 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
225 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
226 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
227 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
228 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
229 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
230 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
231 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
232 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
233 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
234 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
235 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
236 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
237 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
238 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
239 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
241 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
243 /* Channel management */
244 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
245 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
246 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
248 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
249 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
250 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
251 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
252 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
253 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
254 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
256 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
257 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
258 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
259 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
260 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
261 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
262 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
263 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
264 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
265 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
266 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
267 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
268 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
269 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
270 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
271 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
272 { "CSMSG_SET_MODES", "$bModes $b %s" },
273 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
274 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
275 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
276 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
277 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
278 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
279 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
280 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
281 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
282 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
283 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
284 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
285 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
286 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
287 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
288 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
289 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
290 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
292 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
293 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
294 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
295 { "CSMSG_USET_INFO", "$bInfo $b %s" },
297 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
298 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
299 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
300 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
301 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
302 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
303 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
304 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
305 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
306 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
307 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
309 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
310 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
311 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
312 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
313 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
314 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
316 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
317 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
318 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
319 { "CSMSG_PROTECT_NONE", "No users will be protected." },
320 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
321 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
322 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
324 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
325 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
326 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
327 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
328 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
330 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
331 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
332 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
333 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
334 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
336 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
337 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
338 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
339 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
340 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
341 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
343 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
344 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
345 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
346 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
347 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
348 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
349 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
350 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
352 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
353 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
354 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
356 /* Channel userlist */
357 { "CSMSG_ACCESS_ALL_HEADER_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
;
6833 /* Check for bans. If they're joining through a ban, one of two
6835 * 1: Join during a netburst, by riding the break. Kick them
6836 * unless they have ops or voice in the channel.
6837 * 2: They're allowed to join through the ban (an invite in
6838 * ircu2.10, or a +e on Hybrid, or something).
6839 * If they're not joining through a ban, and the banlist is not
6840 * full, see if they're on the banlist for the channel. If so,
6843 if(user
->uplink
->burst
&& !mNode
->modes
)
6846 for(ii
= 0; ii
< channel
->banlist
.used
; ii
++)
6848 if(user_matches_glob(user
, channel
->banlist
.list
[ii
]->ban
, MATCH_USENICK
))
6850 /* Riding a netburst. Naughty. */
6851 KickChannelUser(user
, channel
, chanserv
, "User from far side of netsplit should have been banned - bye.");
6857 mod_chanmode_init(&change
);
6859 if(channel
->banlist
.used
< MAXBANS
)
6861 /* Not joining through a ban. */
6862 for(bData
= cData
->bans
;
6863 bData
&& !user_matches_glob(user
, bData
->mask
, MATCH_USENICK
);
6864 bData
= bData
->next
);
6868 char kick_reason
[MAXLEN
];
6869 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6871 bData
->triggered
= now
;
6872 if(bData
!= cData
->bans
)
6874 /* Shuffle the ban to the head of the list. */
6876 bData
->next
->prev
= bData
->prev
;
6878 bData
->prev
->next
= bData
->next
;
6881 bData
->next
= cData
->bans
;
6884 cData
->bans
->prev
= bData
;
6885 cData
->bans
= bData
;
6888 change
.args
[0].mode
= MODE_BAN
;
6889 change
.args
[0].u
.hostmask
= bData
->mask
;
6890 mod_chanmode_announce(chanserv
, channel
, &change
);
6891 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6896 /* ChanServ will not modify the limits in join-flooded channels.
6897 It will also skip DynLimit processing when the user (or srvx)
6898 is bursting in, because there are likely more incoming. */
6899 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6900 && !user
->uplink
->burst
6901 && !channel
->join_flooded
6902 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6904 /* The user count has begun "bumping" into the channel limit,
6905 so set a timer to raise the limit a bit. Any previous
6906 timers are removed so three incoming users within the delay
6907 results in one limit change, not three. */
6909 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6910 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6913 /* Give automodes exept during join-floods */
6914 if(!channel
->join_flooded
)
6916 if(cData
->chOpts
[chAutomode
] == 'v')
6917 modes
|= MODE_VOICE
;
6918 else if(cData
->chOpts
[chAutomode
] == 'h')
6919 modes
|= MODE_HALFOP
;
6920 else if(cData
->chOpts
[chAutomode
] == 'o')
6921 modes
|= MODE_CHANOP
;
6924 greeting
= cData
->greeting
;
6925 if(user
->handle_info
)
6927 handle
= user
->handle_info
;
6929 if(IsHelper(user
) && !IsHelping(user
))
6932 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6934 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6936 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6942 uData
= GetTrueChannelAccess(cData
, handle
);
6943 if(uData
&& !IsUserSuspended(uData
))
6945 /* non users getting automodes are handled above. */
6946 if(IsUserAutoOp(uData
) && cData
->chOpts
[chAutomode
] != 'n')
6948 if(uData
->access
>= UL_OP
)
6949 modes
|= MODE_CHANOP
;
6950 else if(uData
->access
>= UL_HALFOP
)
6951 modes
|= MODE_HALFOP
;
6952 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chAutomode
] != 'm')
6953 modes
|= MODE_VOICE
;
6955 if(uData
->access
>= UL_PRESENT
)
6956 cData
->visited
= now
;
6957 if(cData
->user_greeting
)
6958 greeting
= cData
->user_greeting
;
6960 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6961 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6969 /* If user joining normally (not during burst), apply op or voice,
6970 * and send greeting/userinfo as appropriate.
6972 if(!user
->uplink
->burst
)
6976 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
6977 if(modes & MODE_CHANOP) {
6978 modes &= ~MODE_HALFOP;
6979 modes &= ~MODE_VOICE;
6982 change
.args
[0].mode
= modes
;
6983 change
.args
[0].u
.member
= mNode
;
6984 mod_chanmode_announce(chanserv
, channel
, &change
);
6987 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6989 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6995 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6997 struct mod_chanmode change
;
6998 struct userData
*channel
;
6999 unsigned int ii
, jj
;
7001 if(!user
->handle_info
)
7004 mod_chanmode_init(&change
);
7006 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
7008 struct chanNode
*cn
;
7009 struct modeNode
*mn
;
7010 if(IsUserSuspended(channel
)
7011 || IsSuspended(channel
->channel
)
7012 || !(cn
= channel
->channel
->channel
))
7015 mn
= GetUserMode(cn
, user
);
7018 if(!IsUserSuspended(channel
)
7019 && IsUserAutoInvite(channel
)
7020 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
7022 && !user
->uplink
->burst
)
7023 irc_invite(chanserv
, user
, cn
);
7027 if(channel
->access
>= UL_PRESENT
)
7028 channel
->channel
->visited
= now
;
7030 if(IsUserAutoOp(channel
))
7032 if(channel
->access
>= UL_OP
)
7033 change
.args
[0].mode
= MODE_CHANOP
;
7034 else if(channel
->access
>= UL_HALFOP
)
7035 change
.args
[0].mode
= MODE_HALFOP
;
7036 else if(channel
->access
>= UL_PEON
)
7037 change
.args
[0].mode
= MODE_VOICE
;
7039 change
.args
[0].mode
= 0;
7040 change
.args
[0].u
.member
= mn
;
7041 if(change
.args
[0].mode
)
7042 mod_chanmode_announce(chanserv
, cn
, &change
);
7045 channel
->seen
= now
;
7046 channel
->present
= 1;
7049 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7051 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
7052 struct banData
*ban
;
7054 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
7055 || !channel
->channel_info
7056 || IsSuspended(channel
->channel_info
))
7058 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7059 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7061 if(jj
< channel
->banlist
.used
)
7063 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
7065 char kick_reason
[MAXLEN
];
7066 if(!user_matches_glob(user
, ban
->mask
,MATCH_USENICK
| MATCH_VISIBLE
))
7068 change
.args
[0].mode
= MODE_BAN
;
7069 change
.args
[0].u
.hostmask
= ban
->mask
;
7070 mod_chanmode_announce(chanserv
, channel
, &change
);
7071 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
7072 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7073 ban
->triggered
= now
;
7078 if(IsSupportHelper(user
))
7080 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7082 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
7084 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
7092 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
7094 struct chanData
*cData
;
7095 struct userData
*uData
;
7097 cData
= mn
->channel
->channel_info
;
7098 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
7101 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
7103 /* Allow for a bit of padding so that the limit doesn't
7104 track the user count exactly, which could get annoying. */
7105 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
7107 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7108 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7112 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
7114 scan_user_presence(uData
, mn
->user
);
7118 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
7120 unsigned int ii
, jj
;
7121 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7123 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
7124 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
7126 if(jj
< mn
->user
->channels
.used
)
7129 if(ii
== chanserv_conf
.support_channels
.used
)
7130 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
7135 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
7137 struct userData
*uData
;
7139 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
7140 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
7141 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
7144 if(protect_user(victim
, kicker
, channel
->channel_info
))
7146 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED_KICK");
7147 KickChannelUser(kicker
, channel
, chanserv
, reason
);
7150 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
7155 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
7157 struct chanData
*cData
;
7159 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
7162 cData
= channel
->channel_info
;
7163 if(bad_topic(channel
, user
, channel
->topic
))
7164 { /* User doesnt have privs to set topics. Undo it */
7165 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
7166 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7169 /* If there is a topic mask set, and the new topic doesnt match,
7170 * set the topic to mask + new_topic */
7171 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
7173 char new_topic
[TOPICLEN
+1];
7174 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
7177 SetChannelTopic(channel
, chanserv
, chanserv
, new_topic
, 1);
7178 /* and fall through to topicsnarf code below.. */
7180 else /* Topic couldnt fit into mask, was too long */
7182 SetChannelTopic(channel
, chanserv
, chanserv
, old_topic
, 1);
7183 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
7184 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
7188 /* With topicsnarf, grab the topic and save it as the default topic. */
7189 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
7192 cData
->topic
= strdup(channel
->topic
);
7198 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
7200 struct mod_chanmode
*bounce
= NULL
;
7201 unsigned int bnc
, ii
;
7204 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
7207 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
7208 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
7210 char correct
[MAXLEN
];
7211 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
7212 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
7213 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
7215 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
7217 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
7219 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7220 if(!protect_user(victim
, user
, channel
->channel_info
))
7223 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7226 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7227 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
7228 if(bounce
->args
[bnc
].u
.member
)
7232 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
7233 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7235 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
7237 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
7239 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
7240 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
7243 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7244 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
7245 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
7248 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
7250 const char *ban
= change
->args
[ii
].u
.hostmask
;
7251 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
7254 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
7255 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
7256 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
7258 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
7263 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
7264 mod_chanmode_announce(chanserv
, channel
, bounce
);
7265 for(ii
= 0; ii
< change
->argc
; ++ii
)
7266 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
7267 free((char*)bounce
->args
[ii
].u
.hostmask
);
7268 mod_chanmode_free(bounce
);
7273 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
7275 struct chanNode
*channel
;
7276 struct banData
*bData
;
7277 struct mod_chanmode change
;
7278 unsigned int ii
, jj
;
7279 char kick_reason
[MAXLEN
];
7281 mod_chanmode_init(&change
);
7283 change
.args
[0].mode
= MODE_BAN
;
7284 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
7286 channel
= user
->channels
.list
[ii
]->channel
;
7287 /* Need not check for bans if they're opped or voiced. */
7288 /* TODO: does this make sense in automode v, h, and o? *
7289 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7290 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
/*|MODE_VOICE */))
7292 /* Need not check for bans unless channel registration is active. */
7293 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
7295 /* Look for a matching ban already on the channel. */
7296 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
7297 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, MATCH_USENICK
))
7299 /* Need not act if we found one. */
7300 if(jj
< channel
->banlist
.used
)
7302 /* Look for a matching ban in this channel. */
7303 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
7305 if(!user_matches_glob(user
, bData
->mask
, MATCH_USENICK
| MATCH_VISIBLE
))
7307 change
.args
[0].u
.hostmask
= bData
->mask
;
7308 mod_chanmode_announce(chanserv
, channel
, &change
);
7309 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
7310 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
7311 bData
->triggered
= now
;
7312 break; /* we don't need to check any more bans in the channel */
7317 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
7319 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
7323 dict_remove2(handle_dnrs
, old_handle
, 1);
7324 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
7325 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
7330 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
7332 struct userNode
*h_user
;
7334 if(handle
->channels
)
7336 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
7337 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
7339 while(handle
->channels
)
7340 del_channel_user(handle
->channels
, 1);
7345 handle_server_link(UNUSED_ARG(struct server
*server
))
7347 struct chanData
*cData
;
7349 for(cData
= channelList
; cData
; cData
= cData
->next
)
7351 if(!IsSuspended(cData
))
7352 cData
->may_opchan
= 1;
7353 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
7354 && !cData
->channel
->join_flooded
7355 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
7356 < chanserv_conf
.adjust_threshold
))
7358 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
7359 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
7365 chanserv_conf_read(void)
7369 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
7370 struct mod_chanmode
*change
;
7371 struct string_list
*strlist
;
7372 struct chanNode
*chan
;
7375 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
7377 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
7380 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7381 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7382 chanserv_conf
.support_channels
.used
= 0;
7383 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
7385 for(ii
= 0; ii
< strlist
->used
; ++ii
)
7387 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7390 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
7392 channelList_append(&chanserv_conf
.support_channels
, chan
);
7395 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
7398 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
7401 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
7403 channelList_append(&chanserv_conf
.support_channels
, chan
);
7405 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
7406 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
7407 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
7408 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
7409 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
7410 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
7411 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
7412 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
7413 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
7414 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
7415 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
7416 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
7417 str
= database_get_data(conf_node
, KEY_BAN_TIMEOUT_FREQ
, RECDB_QSTRING
);
7418 chanserv_conf
.ban_timeout_frequency
= str
? ParseInterval(str
) : 600;
7419 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
7420 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
7421 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
7422 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
7423 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
7424 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
7425 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
7426 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
7427 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
7428 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
7429 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
7431 NickChange(chanserv
, str
, 0);
7432 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
7433 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
7434 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
7435 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
7436 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
7437 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
7438 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
7439 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
7440 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
7441 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
7442 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
7443 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
7444 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
7445 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
7446 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
7447 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
7448 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
7451 safestrncpy(mode_line
, str
, sizeof(mode_line
));
7452 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
7453 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
, 0))
7454 && (change
->argc
< 2))
7456 chanserv_conf
.default_modes
= *change
;
7457 mod_chanmode_free(change
);
7459 free_string_list(chanserv_conf
.set_shows
);
7460 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
7462 strlist
= string_list_copy(strlist
);
7465 static const char *list
[] = {
7466 /* free form text */
7467 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7468 /* options based on user level */
7469 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7470 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7471 /* multiple choice options */
7472 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7473 /* binary options */
7474 "DynLimit", "NoDelete", "BanTimeout",
7479 strlist
= alloc_string_list(ArrayLength(list
)-1);
7480 for(ii
=0; list
[ii
]; ii
++)
7481 string_list_append(strlist
, strdup(list
[ii
]));
7483 chanserv_conf
.set_shows
= strlist
;
7484 /* We don't look things up now, in case the list refers to options
7485 * defined by modules initialized after this point. Just mark the
7486 * function list as invalid, so it will be initialized.
7488 set_shows_list
.used
= 0;
7489 free_string_list(chanserv_conf
.eightball
);
7490 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
7493 strlist
= string_list_copy(strlist
);
7497 strlist
= alloc_string_list(4);
7498 string_list_append(strlist
, strdup("Yes."));
7499 string_list_append(strlist
, strdup("No."));
7500 string_list_append(strlist
, strdup("Maybe so."));
7502 chanserv_conf
.eightball
= strlist
;
7503 free_string_list(chanserv_conf
.old_ban_names
);
7504 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
7506 strlist
= string_list_copy(strlist
);
7508 strlist
= alloc_string_list(2);
7509 chanserv_conf
.old_ban_names
= strlist
;
7510 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
7511 off_channel
= str
? atoi(str
) : 0;
7515 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
7518 struct note_type
*ntype
;
7521 if(!(obj
= GET_RECORD_OBJECT(rd
)))
7523 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
7526 if(!(ntype
= chanserv_create_note_type(key
)))
7528 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
7532 /* Figure out set access */
7533 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
7535 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7536 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
7538 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
7540 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7541 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7543 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7545 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7549 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7550 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7551 ntype
->set_access
.min_opserv
= 0;
7554 /* Figure out visibility */
7555 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7556 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7557 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7558 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7559 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7560 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7561 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7562 ntype
->visible_type
= NOTE_VIS_ALL
;
7564 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7566 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7567 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7571 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7573 struct handle_info
*handle
;
7574 struct userData
*uData
;
7575 char *seen
, *inf
, *flags
, *expires
;
7577 unsigned short access
;
7579 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7581 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7585 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7586 if(access
> UL_OWNER
)
7588 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7592 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7593 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7594 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7595 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7596 expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7597 handle
= get_handle_info(key
);
7600 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7604 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7605 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7606 uData
->expires
= expires
? strtoul(expires
, NULL
, 0) : 0;
7608 if((uData
->flags
& USER_SUSPENDED
) && uData
->expires
)
7610 if(uData
->expires
> now
)
7611 timeq_add(uData
->expires
, chanserv_expire_user_suspension
, uData
);
7613 uData
->flags
&= ~USER_SUSPENDED
;
7616 /* Upgrade: set autoop to the inverse of noautoop */
7617 if(chanserv_read_version
< 2)
7619 /* if noautoop is true, set autoop false, and vice versa */
7620 if(uData
->flags
& USER_NOAUTO_OP
)
7621 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7623 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7624 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
);
7630 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7632 struct banData
*bData
;
7633 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7634 time_t set_time
, triggered_time
, expires_time
;
7636 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7638 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7642 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7643 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7644 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7645 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7646 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7647 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7648 if (!reason
|| !owner
)
7651 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7652 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7654 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7656 expires_time
= set_time
+ atoi(s_duration
);
7660 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7663 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7666 static struct suspended
*
7667 chanserv_read_suspended(dict_t obj
)
7669 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7673 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7674 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7675 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7676 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7677 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7678 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7679 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7680 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7681 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7682 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7686 static struct giveownership
*
7687 chanserv_read_giveownership(dict_t obj
)
7689 struct giveownership
*giveownership
= calloc(1, sizeof(*giveownership
));
7693 str
= database_get_data(obj
, KEY_STAFF_ISSUER
, RECDB_QSTRING
);
7694 giveownership
->staff_issuer
= str
? strdup(str
) : NULL
;
7696 giveownership
->old_owner
= strdup(database_get_data(obj
, KEY_OLD_OWNER
, RECDB_QSTRING
));
7698 giveownership
->target
= strdup(database_get_data(obj
, KEY_TARGET
, RECDB_QSTRING
));
7699 giveownership
->target_access
= atoi(database_get_data(obj
, KEY_TARGET_ACCESS
, RECDB_QSTRING
));
7701 str
= database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
);
7702 giveownership
->reason
= str
? strdup(str
) : NULL
;
7703 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7704 giveownership
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7706 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7707 giveownership
->previous
= previous
? chanserv_read_giveownership(previous
) : NULL
;
7708 return giveownership
;
7712 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7714 struct suspended
*suspended
;
7715 struct giveownership
*giveownership
;
7716 struct mod_chanmode
*modes
;
7717 struct chanNode
*cNode
;
7718 struct chanData
*cData
;
7719 struct dict
*channel
, *obj
;
7720 char *str
, *argv
[10];
7724 channel
= hir
->d
.object
;
7726 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7729 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7732 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7735 cData
= register_channel(cNode
, str
);
7738 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7742 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7744 enum levelOption lvlOpt
;
7745 enum charOption chOpt
;
7747 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7748 cData
->flags
= atoi(str
);
7750 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7752 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7754 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7755 else if(levelOptions
[lvlOpt
].old_flag
)
7757 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7758 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7760 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7764 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7766 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7768 cData
->chOpts
[chOpt
] = str
[0];
7771 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7773 enum levelOption lvlOpt
;
7774 enum charOption chOpt
;
7777 cData
->flags
= base64toint(str
, 5);
7778 count
= strlen(str
+= 5);
7779 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7782 if(levelOptions
[lvlOpt
].old_flag
)
7784 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7785 lvl
= levelOptions
[lvlOpt
].flag_value
;
7787 lvl
= levelOptions
[lvlOpt
].default_value
;
7789 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7791 case 'c': lvl
= UL_COOWNER
; break;
7792 case 'm': lvl
= UL_MANAGER
; break;
7793 case 'n': lvl
= UL_OWNER
+1; break;
7794 case 'o': lvl
= UL_OP
; break;
7795 case 'p': lvl
= UL_PEON
; break;
7796 case 'h': lvl
= UL_HALFOP
; break;
7797 case 'w': lvl
= UL_OWNER
; break;
7798 default: lvl
= 0; break;
7800 cData
->lvlOpts
[lvlOpt
] = lvl
;
7802 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7803 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7806 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7808 suspended
= chanserv_read_suspended(obj
);
7809 cData
->suspended
= suspended
;
7810 suspended
->cData
= cData
;
7811 /* We could use suspended->expires and suspended->revoked to
7812 * set the CHANNEL_SUSPENDED flag, but we don't. */
7814 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7816 suspended
= calloc(1, sizeof(*suspended
));
7817 suspended
->issued
= 0;
7818 suspended
->revoked
= 0;
7819 suspended
->suspender
= strdup(str
);
7820 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7821 suspended
->expires
= str
? atoi(str
) : 0;
7822 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7823 suspended
->reason
= strdup(str
? str
: "No reason");
7824 suspended
->previous
= NULL
;
7825 cData
->suspended
= suspended
;
7826 suspended
->cData
= cData
;
7830 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7831 suspended
= NULL
; /* to squelch a warning */
7834 if(IsSuspended(cData
)) {
7835 if(suspended
->expires
> now
)
7836 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7837 else if(suspended
->expires
)
7838 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7841 if((obj
= database_get_data(hir
->d
.object
, KEY_GIVEOWNERSHIP
, RECDB_OBJECT
)))
7843 giveownership
= chanserv_read_giveownership(obj
);
7844 cData
->giveownership
= giveownership
;
7847 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7848 struct mod_chanmode change
;
7849 mod_chanmode_init(&change
);
7851 change
.args
[0].mode
= MODE_CHANOP
;
7852 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7853 mod_chanmode_announce(chanserv
, cNode
, &change
);
7856 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7857 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7858 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7859 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7860 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7861 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7862 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7863 cData
->max
= str
? atoi(str
) : 0;
7864 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7865 cData
->greeting
= str
? strdup(str
) : NULL
;
7866 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7867 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7868 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7869 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7870 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7871 cData
->topic
= str
? strdup(str
) : NULL
;
7873 if(!IsSuspended(cData
)
7874 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7875 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7876 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
, 0))) {
7877 cData
->modes
= *modes
;
7879 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7880 if(cData
->modes
.argc
> 1)
7881 cData
->modes
.argc
= 1;
7882 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7883 mod_chanmode_free(modes
);
7886 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7887 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7888 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7890 if(!cData
->users
&& !IsProtected(cData
))
7892 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7893 unregister_channel(cData
, "has empty user list.");
7897 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7898 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7899 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7901 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7902 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7904 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7905 struct record_data
*rd
= iter_data(it
);
7906 const char *note
, *setter
;
7908 if(rd
->type
!= RECDB_OBJECT
)
7910 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7914 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7916 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7918 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7922 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7923 if(!setter
) setter
= "<unknown>";
7924 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7932 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7934 const char *setter
, *reason
, *str
;
7935 struct do_not_register
*dnr
;
7937 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7940 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7943 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7946 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7949 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7952 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7954 dnr
->set
= atoi(str
);
7960 chanserv_version_read(struct dict
*section
)
7964 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7966 chanserv_read_version
= atoi(str
);
7967 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7971 chanserv_saxdb_read(struct dict
*database
)
7973 struct dict
*section
;
7976 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7977 chanserv_version_read(section
);
7979 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7980 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7981 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7983 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7984 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7985 chanserv_channel_read(iter_key(it
), iter_data(it
));
7987 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7988 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7989 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7995 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7997 int high_present
= 0;
7998 saxdb_start_record(ctx
, KEY_USERS
, 1);
7999 for(; uData
; uData
= uData
->next
)
8001 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
8003 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
8004 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
8005 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
8007 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
8009 saxdb_write_int(ctx
, KEY_EXPIRES
, uData
->expires
);
8011 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
8012 saxdb_end_record(ctx
);
8014 saxdb_end_record(ctx
);
8015 return high_present
;
8019 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
8023 saxdb_start_record(ctx
, KEY_BANS
, 1);
8024 for(; bData
; bData
= bData
->next
)
8026 saxdb_start_record(ctx
, bData
->mask
, 0);
8027 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
8028 if(bData
->triggered
)
8029 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
8031 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
8033 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
8035 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
8036 saxdb_end_record(ctx
);
8038 saxdb_end_record(ctx
);
8042 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
8044 saxdb_start_record(ctx
, name
, 0);
8045 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
8046 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
8048 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
8050 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
8052 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
8054 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
8055 saxdb_end_record(ctx
);
8059 chanserv_write_giveownership(struct saxdb_context
*ctx
, const char *name
, struct giveownership
*giveownership
)
8061 saxdb_start_record(ctx
, name
, 0);
8062 if(giveownership
->staff_issuer
)
8063 saxdb_write_string(ctx
, KEY_STAFF_ISSUER
, giveownership
->staff_issuer
);
8064 if(giveownership
->old_owner
)
8065 saxdb_write_string(ctx
, KEY_OLD_OWNER
, giveownership
->old_owner
);
8066 if(giveownership
->target
)
8067 saxdb_write_string(ctx
, KEY_TARGET
, giveownership
->target
);
8068 if(giveownership
->target_access
)
8069 saxdb_write_int(ctx
, KEY_TARGET_ACCESS
, giveownership
->target_access
);
8070 if(giveownership
->reason
)
8071 saxdb_write_string(ctx
, KEY_REASON
, giveownership
->reason
);
8072 if(giveownership
->issued
)
8073 saxdb_write_int(ctx
, KEY_ISSUED
, giveownership
->issued
);
8074 if(giveownership
->previous
)
8075 chanserv_write_giveownership(ctx
, KEY_PREVIOUS
, giveownership
->previous
);
8076 saxdb_end_record(ctx
);
8080 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
8084 enum levelOption lvlOpt
;
8085 enum charOption chOpt
;
8087 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
8089 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
8090 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
8092 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
8093 if(channel
->registrar
)
8094 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
8095 if(channel
->greeting
)
8096 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
8097 if(channel
->user_greeting
)
8098 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
8099 if(channel
->topic_mask
)
8100 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
8101 if(channel
->suspended
)
8102 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
8103 if(channel
->giveownership
)
8104 chanserv_write_giveownership(ctx
, "giveownership", channel
->giveownership
);
8106 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
8107 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
8108 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
8109 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
8110 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
8112 buf
[0] = channel
->chOpts
[chOpt
];
8114 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
8116 saxdb_end_record(ctx
);
8118 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
8120 mod_chanmode_format(&channel
->modes
, buf
);
8121 saxdb_write_string(ctx
, KEY_MODES
, buf
);
8124 high_present
= chanserv_write_users(ctx
, channel
->users
);
8125 chanserv_write_bans(ctx
, channel
->bans
);
8127 if(dict_size(channel
->notes
))
8131 saxdb_start_record(ctx
, KEY_NOTES
, 1);
8132 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
8134 struct note
*note
= iter_data(it
);
8135 saxdb_start_record(ctx
, iter_key(it
), 0);
8136 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
8137 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
8138 saxdb_end_record(ctx
);
8140 saxdb_end_record(ctx
);
8143 if(channel
->ownerTransfer
)
8144 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
8145 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
8146 saxdb_end_record(ctx
);
8150 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
8154 saxdb_start_record(ctx
, ntype
->name
, 0);
8155 switch(ntype
->set_access_type
)
8157 case NOTE_SET_CHANNEL_ACCESS
:
8158 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
8160 case NOTE_SET_CHANNEL_SETTER
:
8161 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
8163 case NOTE_SET_PRIVILEGED
: default:
8164 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
8167 switch(ntype
->visible_type
)
8169 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
8170 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
8171 case NOTE_VIS_PRIVILEGED
:
8172 default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
8174 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
8175 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
8176 saxdb_end_record(ctx
);
8180 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
8182 struct do_not_register
*dnr
;
8185 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
8187 dnr
= iter_data(it
);
8188 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
8190 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
8191 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
8192 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
8193 saxdb_end_record(ctx
);
8198 chanserv_saxdb_write(struct saxdb_context
*ctx
)
8201 struct chanData
*channel
;
8203 /* Version Control*/
8204 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
8205 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
8206 saxdb_end_record(ctx
);
8209 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
8210 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
8211 chanserv_write_note_type(ctx
, iter_data(it
));
8212 saxdb_end_record(ctx
);
8215 saxdb_start_record(ctx
, KEY_DNR
, 1);
8216 write_dnrs_helper(ctx
, handle_dnrs
);
8217 write_dnrs_helper(ctx
, plain_dnrs
);
8218 write_dnrs_helper(ctx
, mask_dnrs
);
8219 saxdb_end_record(ctx
);
8222 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
8223 for(channel
= channelList
; channel
; channel
= channel
->next
)
8224 chanserv_write_channel(ctx
, channel
);
8225 saxdb_end_record(ctx
);
8231 chanserv_db_cleanup(void) {
8233 unreg_part_func(handle_part
);
8235 unregister_channel(channelList
, "terminating.");
8236 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
8237 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
8238 free(chanserv_conf
.support_channels
.list
);
8239 dict_delete(handle_dnrs
);
8240 dict_delete(plain_dnrs
);
8241 dict_delete(mask_dnrs
);
8242 dict_delete(note_types
);
8243 free_string_list(chanserv_conf
.eightball
);
8244 free_string_list(chanserv_conf
.old_ban_names
);
8245 free_string_list(chanserv_conf
.set_shows
);
8246 free(set_shows_list
.list
);
8247 free(uset_shows_list
.list
);
8250 struct userData
*helper
= helperList
;
8251 helperList
= helperList
->next
;
8256 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8257 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8258 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8261 init_chanserv(const char *nick
)
8263 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
8264 conf_register_reload(chanserv_conf_read
);
8266 reg_server_link_func(handle_server_link
);
8268 reg_new_channel_func(handle_new_channel
);
8269 reg_join_func(handle_join
);
8270 reg_part_func(handle_part
);
8271 reg_kick_func(handle_kick
);
8272 reg_topic_func(handle_topic
);
8273 reg_mode_change_func(handle_mode
);
8274 reg_nick_change_func(handle_nick_change
);
8276 reg_auth_func(handle_auth
);
8277 reg_handle_rename_func(handle_rename
);
8278 reg_unreg_func(handle_unreg
);
8280 handle_dnrs
= dict_new();
8281 dict_set_free_data(handle_dnrs
, free
);
8282 plain_dnrs
= dict_new();
8283 dict_set_free_data(plain_dnrs
, free
);
8284 mask_dnrs
= dict_new();
8285 dict_set_free_data(mask_dnrs
, free
);
8287 reg_svccmd_unbind_func(handle_svccmd_unbind
);
8288 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
8289 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
8290 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8291 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
8292 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
8293 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8294 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
8295 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
8296 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
8298 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
8300 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
8301 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
8303 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8304 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8305 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8306 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8307 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8309 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
8310 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
8311 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
8312 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8313 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8314 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8316 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8317 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
8318 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8319 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
8321 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
8322 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
8323 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8324 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8325 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8326 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
8327 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8328 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8329 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8330 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
8332 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8333 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8334 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
8335 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
8336 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
8337 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
8338 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
8339 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
8340 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
8341 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
8342 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
8343 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
8344 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8345 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
8347 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8348 DEFINE_COMMAND(last
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
8349 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8350 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8352 /* if you change dellamer access, see also places
8353 * like unbanme which have manager hardcoded. */
8354 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
8355 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
8357 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
8359 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
8361 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
8362 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8363 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8364 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8365 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8366 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8367 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8368 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8369 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8370 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8371 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8372 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
8374 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
8375 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
8377 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
8378 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
8379 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
8380 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
8382 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8383 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
8384 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
8385 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
8386 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
8388 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8389 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8390 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8391 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8392 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8393 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8394 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
8396 /* Channel options */
8397 DEFINE_CHANNEL_OPTION(defaulttopic
);
8398 DEFINE_CHANNEL_OPTION(topicmask
);
8399 DEFINE_CHANNEL_OPTION(greeting
);
8400 DEFINE_CHANNEL_OPTION(usergreeting
);
8401 DEFINE_CHANNEL_OPTION(modes
);
8402 DEFINE_CHANNEL_OPTION(enfops
);
8403 DEFINE_CHANNEL_OPTION(enfhalfops
);
8404 DEFINE_CHANNEL_OPTION(automode
);
8405 DEFINE_CHANNEL_OPTION(protect
);
8406 DEFINE_CHANNEL_OPTION(enfmodes
);
8407 DEFINE_CHANNEL_OPTION(enftopic
);
8408 DEFINE_CHANNEL_OPTION(pubcmd
);
8409 DEFINE_CHANNEL_OPTION(userinfo
);
8410 DEFINE_CHANNEL_OPTION(dynlimit
);
8411 DEFINE_CHANNEL_OPTION(topicsnarf
);
8412 DEFINE_CHANNEL_OPTION(nodelete
);
8413 DEFINE_CHANNEL_OPTION(toys
);
8414 DEFINE_CHANNEL_OPTION(setters
);
8415 DEFINE_CHANNEL_OPTION(topicrefresh
);
8416 DEFINE_CHANNEL_OPTION(ctcpreaction
);
8417 DEFINE_CHANNEL_OPTION(bantimeout
);
8418 DEFINE_CHANNEL_OPTION(inviteme
);
8420 DEFINE_CHANNEL_OPTION(offchannel
);
8421 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
8423 /* Alias set topic to set defaulttopic for compatibility. */
8424 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
8427 DEFINE_USER_OPTION(autoinvite
);
8428 DEFINE_USER_OPTION(info
);
8429 DEFINE_USER_OPTION(autoop
);
8431 /* Alias uset autovoice to uset autoop. */
8432 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
8434 note_types
= dict_new();
8435 dict_set_free_data(note_types
, chanserv_deref_note_type
);
8438 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
8439 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
8440 service_register(chanserv
)->trigger
= '!';
8441 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
8444 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
8446 if(chanserv_conf
.channel_expire_frequency
)
8447 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
8449 if(chanserv_conf
.ban_timeout_frequency
)
8450 timeq_add(now
+ chanserv_conf
.ban_timeout_frequency
, expire_bans
, NULL
);
8452 if(chanserv_conf
.refresh_period
)
8454 time_t next_refresh
;
8455 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
8456 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
8459 reg_exit_func(chanserv_db_cleanup
);
8460 message_register_table(msgtab
);