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_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_VERSION_CONTROL "version_control"
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* version control paramiter */
64 #define KEY_VERSION_NUMBER "version_number"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans" /* for lamers */
103 #define KEY_MAX "max"
104 #define KEY_NOTES "notes"
105 #define KEY_TOPIC_MASK "topic_mask"
106 #define KEY_OWNER_TRANSFER "owner_transfer"
109 #define KEY_LEVEL "level"
110 #define KEY_INFO "info"
111 #define KEY_SEEN "seen"
114 #define KEY_OWNER "owner"
115 #define KEY_REASON "reason"
116 #define KEY_SET "set"
117 #define KEY_DURATION "duration"
118 #define KEY_EXPIRES "expires"
119 #define KEY_TRIGGERED "triggered"
121 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
122 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
124 /* Administrative messages */
125 static const struct message_entry msgtab
[] = {
126 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
128 /* Channel registration */
129 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
130 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
131 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
132 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
133 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
134 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
135 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
136 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
138 /* Do-not-register channels */
139 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
140 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
141 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
142 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
143 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
144 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
145 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
146 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
147 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
148 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
149 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
151 /* Channel unregistration */
152 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
153 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
154 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
155 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
158 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
159 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
161 /* Channel merging */
162 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
163 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
164 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
165 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
166 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
168 /* Handle unregistration */
169 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
172 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
173 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
174 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
175 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
176 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
177 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
178 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
179 { "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." },
180 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
181 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
182 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
183 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
184 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
185 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
187 /* Removing yourself from a channel. */
188 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
189 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
190 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
192 /* User management */
193 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
194 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
195 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
196 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
197 { "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." },
198 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
199 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
200 { "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." },
201 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
202 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
203 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
204 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
205 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
206 { "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" },
207 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
209 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
210 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
211 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
212 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
213 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
214 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
217 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
218 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
219 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
220 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
221 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
222 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
223 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
224 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
225 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
226 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
227 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
228 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
229 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
230 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
231 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
232 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
233 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
235 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
237 /* Channel management */
238 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
239 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
240 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
242 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
243 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
244 { "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" },
245 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
246 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
247 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
248 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
250 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
251 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
252 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
253 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
254 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
255 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
256 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
257 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
258 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
260 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
261 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
262 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
264 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
265 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
266 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
267 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
268 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
269 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
270 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
271 { "CSMSG_SET_MODES", "$bModes $b %s" },
272 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
273 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
274 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
275 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
277 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
278 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
280 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
281 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
282 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
283 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
285 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
287 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
288 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
289 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
290 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
291 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
292 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
293 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
294 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
295 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
296 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
297 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
298 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
299 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
300 { "CSMSG_USET_INFO", "$bInfo $b %s" },
302 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
303 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
304 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
305 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
306 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
307 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
308 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
309 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
310 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
311 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
312 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
313 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
314 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
315 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
316 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
317 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
318 { "CSMSG_PROTECT_NONE", "No users will be protected." },
319 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
320 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
321 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
322 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
323 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
324 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
325 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
326 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
327 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
329 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
330 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
332 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
333 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
334 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
335 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
336 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
337 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
338 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
339 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
341 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
342 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
343 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
345 /* Channel userlist */
346 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
347 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
348 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
349 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
350 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
352 /* Channel note list */
353 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
354 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
355 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
356 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
357 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
358 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
359 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
360 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
361 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
362 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
363 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
364 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
365 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
366 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
367 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
369 /* Channel [un]suspension */
370 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
371 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
372 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
373 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
374 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
375 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
376 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
378 /* Access information */
379 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
380 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
381 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
382 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
383 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
384 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
385 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
386 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
387 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
388 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
389 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
391 /* Seen information */
392 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
393 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
394 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
395 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
397 /* Names information */
398 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
399 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
401 /* Channel information */
402 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
403 { "CSMSG_BAR", "----------------------------------------"},
404 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
405 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
406 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
407 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
408 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
409 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
410 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
411 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
412 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
413 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
414 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
417 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
418 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
422 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
423 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
424 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
426 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
427 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
428 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
429 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
430 { "CSMSG_PEEK_OPS", "$bOps:$b" },
431 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
432 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
434 /* Network information */
435 { "CSMSG_NETWORK_INFO", "Network Information:" },
436 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
437 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
438 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
439 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
440 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
441 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
442 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
443 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
446 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
447 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
448 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
450 /* Channel searches */
451 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
452 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
453 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
454 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
456 /* Channel configuration */
457 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
458 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
459 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
460 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
463 { "CSMSG_USER_OPTIONS", "User Options:" },
464 { "CSMSG_USER_PROTECTED", "That user is protected." },
467 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
468 { "CSMSG_PING_RESPONSE", "Pong!" },
469 { "CSMSG_WUT_RESPONSE", "wut" },
470 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
471 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
472 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
473 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
474 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
475 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
476 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
479 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
483 /* eject_user and unban_user flags */
484 #define ACTION_KICK 0x0001
485 #define ACTION_BAN 0x0002
486 #define ACTION_ADD_LAMER 0x0004
487 #define ACTION_ADD_TIMED_LAMER 0x0008
488 #define ACTION_UNBAN 0x0010
489 #define ACTION_DEL_LAMER 0x0020
491 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
492 #define MODELEN 40 + KEYLEN
496 #define CSFUNC_ARGS user, channel, argc, argv, cmd
498 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
499 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
500 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
501 reply("MSG_MISSING_PARAMS", argv[0]); \
505 DECLARE_LIST(dnrList
, struct do_not_register
*);
506 DEFINE_LIST(dnrList
, struct do_not_register
*);
508 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
510 struct userNode
*chanserv
;
513 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
514 static struct log_type
*CS_LOG
;
515 struct adduserPending
* adduser_pendings
= NULL
;
516 unsigned int adduser_pendings_count
= 0;
520 struct channelList support_channels
;
521 struct mod_chanmode default_modes
;
523 unsigned long db_backup_frequency
;
524 unsigned long channel_expire_frequency
;
527 unsigned int adjust_delay
;
528 long channel_expire_delay
;
529 unsigned int nodelete_level
;
531 unsigned int adjust_threshold
;
532 int join_flood_threshold
;
534 unsigned int greeting_length
;
535 unsigned int refresh_period
;
536 unsigned int giveownership_period
;
538 unsigned int max_owned
;
539 unsigned int max_chan_users
;
540 unsigned int max_chan_bans
; /* lamers */
541 unsigned int max_userinfo_length
;
543 struct string_list
*set_shows
;
544 struct string_list
*eightball
;
545 struct string_list
*old_ban_names
;
547 const char *ctcp_short_ban_duration
;
548 const char *ctcp_long_ban_duration
;
550 const char *irc_operator_epithet
;
551 const char *network_helper_epithet
;
552 const char *support_helper_epithet
;
557 struct userNode
*user
;
558 struct userNode
*bot
;
559 struct chanNode
*channel
;
561 unsigned short lowest
;
562 unsigned short highest
;
563 struct userData
**users
;
564 struct helpfile_table table
;
567 enum note_access_type
569 NOTE_SET_CHANNEL_ACCESS
,
570 NOTE_SET_CHANNEL_SETTER
,
574 enum note_visible_type
577 NOTE_VIS_CHANNEL_USERS
,
583 enum note_access_type set_access_type
;
585 unsigned int min_opserv
;
586 unsigned short min_ulevel
;
588 enum note_visible_type visible_type
;
589 unsigned int max_length
;
596 struct note_type
*type
;
597 char setter
[NICKSERV_HANDLE_LEN
+1];
601 static unsigned int registered_channels
;
602 static unsigned int banCount
;
604 static const struct {
607 unsigned short level
;
609 } accessLevels
[] = { /* MUST be orderd less to most! */
610 { "peon", "Peon", UL_PEON
, '+' },
611 { "halfop", "HalfOp", UL_HALFOP
, '%' },
612 { "op", "Op", UL_OP
, '@' },
613 { "manager", "Manager", UL_MANAGER
, '%' },
614 { "coowner", "Coowner", UL_COOWNER
, '*' },
615 { "owner", "Owner", UL_OWNER
, '!' },
616 { "helper", "BUG:", UL_HELPER
, 'X' }
619 static const struct {
622 unsigned short default_value
;
623 unsigned int old_idx
;
624 unsigned int old_flag
;
625 unsigned short flag_value
;
627 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
628 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
629 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
630 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
631 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
632 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
633 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
634 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
635 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
636 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
637 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
638 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
639 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
642 struct charOptionValues
{
646 { 'n', "CSMSG_VOICE_NONE" },
647 { 'p', "CSMSG_VOICE_PEON" },
648 { 'a', "CSMSG_VOICE_ALL" }
649 }, protectValues
[] = {
650 { 'a', "CSMSG_PROTECT_ALL" },
651 { 'e', "CSMSG_PROTECT_EQUAL" },
652 { 'l', "CSMSG_PROTECT_LOWER" },
653 { 'n', "CSMSG_PROTECT_NONE" }
655 { 'd', "CSMSG_TOYS_DISABLED" },
656 { 'n', "CSMSG_TOYS_PRIVATE" },
657 { 'p', "CSMSG_TOYS_PUBLIC" }
658 }, topicRefreshValues
[] = {
659 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
660 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
661 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
662 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
663 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
664 }, ctcpReactionValues
[] = {
665 { 'k', "CSMSG_CTCPREACTION_KICK" },
666 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
667 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
668 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
671 static const struct {
675 unsigned int old_idx
;
677 struct charOptionValues
*values
;
679 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues
), voiceValues
},
680 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
681 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
682 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
683 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
686 struct userData
*helperList
;
687 struct chanData
*channelList
;
688 static struct module *chanserv_module
;
689 static unsigned int userCount
;
690 unsigned int chanserv_read_version
= 0; /* db version control */
692 #define CHANSERV_DB_VERSION 2
694 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
695 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
696 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
699 user_level_from_name(const char *name
, unsigned short clamp_level
)
701 unsigned int level
= 0, ii
;
703 level
= strtoul(name
, NULL
, 10);
704 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
705 if(!irccasecmp(name
, accessLevels
[ii
].name
))
706 level
= accessLevels
[ii
].level
;
707 if(level
> clamp_level
)
713 user_level_name_from_level(int level
)
721 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
722 if(level
>= accessLevels
[ii
].level
)
723 highest
= accessLevels
[ii
].title
;
729 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
732 *minl
= strtoul(arg
, &sep
, 10);
740 *maxl
= strtoul(sep
+1, &sep
, 10);
748 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
750 struct userData
*uData
, **head
;
752 if(!channel
|| !handle
)
755 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
756 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
758 for(uData
= helperList
;
759 uData
&& uData
->handle
!= handle
;
760 uData
= uData
->next
);
764 uData
= calloc(1, sizeof(struct userData
));
765 uData
->handle
= handle
;
767 uData
->access
= UL_HELPER
;
773 uData
->next
= helperList
;
775 helperList
->prev
= uData
;
783 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
784 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
787 head
= &(channel
->users
);
790 if(uData
&& (uData
!= *head
))
792 /* Shuffle the user to the head of whatever list he was in. */
794 uData
->next
->prev
= uData
->prev
;
796 uData
->prev
->next
= uData
->next
;
802 (**head
).prev
= uData
;
809 /* Returns non-zero if user has at least the minimum access.
810 * exempt_owner is set when handling !set, so the owner can set things
813 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
815 struct userData
*uData
;
816 struct chanData
*cData
= channel
->channel_info
;
817 unsigned short minimum
= cData
->lvlOpts
[opt
];
820 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
823 if(minimum
<= uData
->access
)
825 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
830 /* Scan for other users authenticated to the same handle
831 still in the channel. If so, keep them listed as present.
833 user is optional, if not null, it skips checking that userNode
834 (for the handle_part function) */
836 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
840 if(IsSuspended(uData
->channel
)
841 || IsUserSuspended(uData
)
842 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
854 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
856 unsigned int eflags
, argc
;
858 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
860 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
861 if(!channel
->channel_info
862 || IsSuspended(channel
->channel_info
)
864 || !ircncasecmp(text
, "ACTION ", 7))
866 /* Figure out the minimum level needed to CTCP the channel */
867 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
869 /* We need to enforce against them; do so. */
872 argv
[1] = user
->nick
;
874 if(GetUserMode(channel
, user
))
875 eflags
|= ACTION_KICK
;
876 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
877 default: case 'k': /* just do the kick */ break;
879 eflags
|= ACTION_BAN
;
882 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
883 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
886 eflags
|= ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
;
887 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
890 argv
[argc
++] = bad_ctcp_reason
;
891 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
895 chanserv_create_note_type(const char *name
)
897 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
898 strcpy(ntype
->name
, name
);
900 dict_insert(note_types
, ntype
->name
, ntype
);
905 chanserv_deref_note_type(void *data
)
907 struct note_type
*ntype
= data
;
909 if(--ntype
->refs
> 0)
915 chanserv_flush_note_type(struct note_type
*ntype
)
917 struct chanData
*cData
;
918 for(cData
= channelList
; cData
; cData
= cData
->next
)
919 dict_remove(cData
->notes
, ntype
->name
);
923 chanserv_truncate_notes(struct note_type
*ntype
)
925 struct chanData
*cData
;
927 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
929 for(cData
= channelList
; cData
; cData
= cData
->next
) {
930 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
933 if(strlen(note
->note
) <= ntype
->max_length
)
935 dict_remove2(cData
->notes
, ntype
->name
, 1);
936 note
= realloc(note
, size
);
937 note
->note
[ntype
->max_length
] = 0;
938 dict_insert(cData
->notes
, ntype
->name
, note
);
942 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
945 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
948 unsigned int len
= strlen(text
);
950 if(len
> type
->max_length
) len
= type
->max_length
;
951 note
= calloc(1, sizeof(*note
) + len
);
953 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
954 memcpy(note
->note
, text
, len
);
956 dict_insert(channel
->notes
, type
->name
, note
);
962 chanserv_free_note(void *data
)
964 struct note
*note
= data
;
966 chanserv_deref_note_type(note
->type
);
967 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
971 static MODCMD_FUNC(cmd_createnote
) {
972 struct note_type
*ntype
;
973 unsigned int arg
= 1, existed
= 0, max_length
;
975 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
978 ntype
= chanserv_create_note_type(argv
[arg
]);
979 if(!irccasecmp(argv
[++arg
], "privileged"))
982 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
983 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
985 else if(!irccasecmp(argv
[arg
], "channel"))
987 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
990 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
993 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
994 ntype
->set_access
.min_ulevel
= ulvl
;
996 else if(!irccasecmp(argv
[arg
], "setter"))
998 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
1002 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1006 if(!irccasecmp(argv
[++arg
], "privileged"))
1007 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1008 else if(!irccasecmp(argv
[arg
], "channel_users"))
1009 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1010 else if(!irccasecmp(argv
[arg
], "all"))
1011 ntype
->visible_type
= NOTE_VIS_ALL
;
1013 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1017 if((arg
+1) >= argc
) {
1018 reply("MSG_MISSING_PARAMS", argv
[0]);
1021 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1022 if(max_length
< 20 || max_length
> 450)
1024 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1027 if(existed
&& (max_length
< ntype
->max_length
))
1029 ntype
->max_length
= max_length
;
1030 chanserv_truncate_notes(ntype
);
1032 ntype
->max_length
= max_length
;
1035 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1037 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1042 dict_remove(note_types
, ntype
->name
);
1046 static MODCMD_FUNC(cmd_removenote
) {
1047 struct note_type
*ntype
;
1050 ntype
= dict_find(note_types
, argv
[1], NULL
);
1051 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1054 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1061 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1064 chanserv_flush_note_type(ntype
);
1066 dict_remove(note_types
, argv
[1]);
1067 reply("CSMSG_NOTE_DELETED", argv
[1]);
1072 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1076 if(orig
->modes_set
& change
->modes_clear
)
1078 if(orig
->modes_clear
& change
->modes_set
)
1080 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1081 && strcmp(orig
->new_key
, change
->new_key
))
1083 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1084 && (orig
->new_limit
!= change
->new_limit
))
1089 static char max_length_text
[MAXLEN
+1][16];
1091 static struct helpfile_expansion
1092 chanserv_expand_variable(const char *variable
)
1094 struct helpfile_expansion exp
;
1096 if(!irccasecmp(variable
, "notes"))
1099 exp
.type
= HF_TABLE
;
1100 exp
.value
.table
.length
= 1;
1101 exp
.value
.table
.width
= 3;
1102 exp
.value
.table
.flags
= 0;
1103 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1104 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1105 exp
.value
.table
.contents
[0][0] = "Note Type";
1106 exp
.value
.table
.contents
[0][1] = "Visibility";
1107 exp
.value
.table
.contents
[0][2] = "Max Length";
1108 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1110 struct note_type
*ntype
= iter_data(it
);
1113 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1114 row
= exp
.value
.table
.length
++;
1115 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1116 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1117 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1118 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1120 if(!max_length_text
[ntype
->max_length
][0])
1121 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1122 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1127 exp
.type
= HF_STRING
;
1128 exp
.value
.str
= NULL
;
1132 static struct chanData
*
1133 register_channel(struct chanNode
*cNode
, char *registrar
)
1135 struct chanData
*channel
;
1136 enum levelOption lvlOpt
;
1137 enum charOption chOpt
;
1139 channel
= calloc(1, sizeof(struct chanData
));
1141 channel
->notes
= dict_new();
1142 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1144 channel
->registrar
= strdup(registrar
);
1145 channel
->registered
= now
;
1146 channel
->visited
= now
;
1147 channel
->limitAdjusted
= now
;
1148 channel
->ownerTransfer
= now
;
1149 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1150 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1151 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1152 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1153 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1155 channel
->prev
= NULL
;
1156 channel
->next
= channelList
;
1159 channelList
->prev
= channel
;
1160 channelList
= channel
;
1161 registered_channels
++;
1163 channel
->channel
= cNode
;
1165 cNode
->channel_info
= channel
;
1170 static struct userData
*
1171 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1173 struct userData
*ud
;
1175 if(access
> UL_OWNER
)
1178 ud
= calloc(1, sizeof(*ud
));
1179 ud
->channel
= channel
;
1180 ud
->handle
= handle
;
1182 ud
->access
= access
;
1183 ud
->info
= info
? strdup(info
) : NULL
;
1186 ud
->next
= channel
->users
;
1188 channel
->users
->prev
= ud
;
1189 channel
->users
= ud
;
1191 channel
->userCount
++;
1195 ud
->u_next
= ud
->handle
->channels
;
1197 ud
->u_next
->u_prev
= ud
;
1198 ud
->handle
->channels
= ud
;
1200 ud
->flags
= USER_FLAGS_DEFAULT
;
1204 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1207 del_channel_user(struct userData
*user
, int do_gc
)
1209 struct chanData
*channel
= user
->channel
;
1211 channel
->userCount
--;
1215 user
->prev
->next
= user
->next
;
1217 channel
->users
= user
->next
;
1219 user
->next
->prev
= user
->prev
;
1222 user
->u_prev
->u_next
= user
->u_next
;
1224 user
->handle
->channels
= user
->u_next
;
1226 user
->u_next
->u_prev
= user
->u_prev
;
1230 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1231 unregister_channel(channel
, "lost all users.");
1234 static struct adduserPending
*
1235 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1237 struct adduserPending
*ap
;
1238 ap
= calloc(1,sizeof(struct adduserPending
));
1239 ap
->channel
= channel
;
1242 ap
->created
= time(NULL
);
1244 /* ap->prev defaults to NULL already.. */
1245 ap
->next
= adduser_pendings
;
1246 if(adduser_pendings
)
1247 adduser_pendings
->prev
= ap
;
1248 adduser_pendings
= ap
;
1249 adduser_pendings_count
++;
1254 del_adduser_pending(struct adduserPending
*ap
)
1257 ap
->prev
->next
= ap
->next
;
1259 adduser_pendings
= ap
->next
;
1262 ap
->next
->prev
= ap
->prev
;
1266 static void expire_adduser_pending();
1268 /* find_adduser_pending(channel, user) will find an arbitrary record
1269 * from user, channel, or user and channel.
1270 * if user or channel are NULL, they will match any records.
1272 static struct adduserPending
*
1273 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1275 struct adduserPending
*ap
;
1277 expire_adduser_pending(); /* why not here.. */
1279 if(!channel
&& !user
) /* 2 nulls matches all */
1280 return(adduser_pendings
);
1281 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1283 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1290 /* Remove all pendings for a user or channel
1292 * called in nickserv.c DelUser() and proto-* unregister_channel()
1295 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1297 struct adduserPending
*ap
;
1299 /* So this is a bit wastefull, i hate dealing with linked lists.
1300 * if its a problem we'll rewrite it right */
1301 while((ap
= find_adduser_pending(channel
, user
))) {
1302 del_adduser_pending(ap
);
1306 /* Called from nickserv.c cmd_auth after someone auths */
1308 process_adduser_pending(struct userNode
*user
)
1310 struct adduserPending
*ap
;
1311 if(!user
->handle_info
)
1312 return; /* not associated with an account */
1313 while((ap
= find_adduser_pending(NULL
, user
)))
1315 struct userData
*actee
;
1316 if(GetTrueChannelAccess(ap
->channel
->channel_info
, ap
->user
->handle_info
))
1318 /* Already on the userlist. do nothing*/
1322 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1323 scan_user_presence(actee
, NULL
);
1325 del_adduser_pending(ap
);
1330 expire_adduser_pending()
1332 struct adduserPending
*ap
, *ap_next
;
1333 ap
= adduser_pendings
;
1336 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1338 ap_next
= ap
->next
; /* save next */
1339 del_adduser_pending(ap
); /* free and relink */
1340 ap
= ap_next
; /* advance */
1347 static void expire_ban(void *data
);
1349 static struct banData
*
1350 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1353 unsigned int ii
, l1
, l2
;
1358 bd
= malloc(sizeof(struct banData
));
1360 bd
->channel
= channel
;
1362 bd
->triggered
= triggered
;
1363 bd
->expires
= expires
;
1365 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1367 extern const char *hidden_host_suffix
;
1368 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1372 l2
= strlen(old_name
);
1375 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1377 new_mask
= alloca(MAXLEN
);
1378 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1381 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1383 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1384 bd
->reason
= strdup(reason
);
1387 timeq_add(expires
, expire_ban
, bd
);
1390 bd
->next
= channel
->bans
; /* lamers */
1392 channel
->bans
->prev
= bd
;
1394 channel
->banCount
++;
1401 del_channel_ban(struct banData
*ban
)
1403 ban
->channel
->banCount
--;
1407 ban
->prev
->next
= ban
->next
;
1409 ban
->channel
->bans
= ban
->next
;
1412 ban
->next
->prev
= ban
->prev
;
1415 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1424 expire_ban(void *data
) /* lamer.. */
1426 struct banData
*bd
= data
;
1427 if(!IsSuspended(bd
->channel
))
1429 struct banList bans
;
1430 struct mod_chanmode change
;
1432 bans
= bd
->channel
->channel
->banlist
;
1433 mod_chanmode_init(&change
);
1434 for(ii
=0; ii
<bans
.used
; ii
++)
1436 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1439 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1440 change
.args
[0].u
.hostmask
= bd
->mask
;
1441 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1447 del_channel_ban(bd
);
1450 static void chanserv_expire_suspension(void *data
);
1453 unregister_channel(struct chanData
*channel
, const char *reason
)
1455 struct mod_chanmode change
;
1456 char msgbuf
[MAXLEN
];
1458 /* After channel unregistration, the following must be cleaned
1460 - Channel information.
1462 - Channel bans. (lamers)
1463 - Channel suspension data.
1464 - adduser_pending data.
1465 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1471 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1475 mod_chanmode_init(&change
);
1476 change
.modes_clear
|= MODE_REGISTERED
;
1477 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1480 wipe_adduser_pending(channel
->channel
, NULL
);
1482 while(channel
->users
)
1483 del_channel_user(channel
->users
, 0);
1485 while(channel
->bans
)
1486 del_channel_ban(channel
->bans
);
1488 free(channel
->topic
);
1489 free(channel
->registrar
);
1490 free(channel
->greeting
);
1491 free(channel
->user_greeting
);
1492 free(channel
->topic_mask
);
1495 channel
->prev
->next
= channel
->next
;
1497 channelList
= channel
->next
;
1500 channel
->next
->prev
= channel
->prev
;
1502 if(channel
->suspended
)
1504 struct chanNode
*cNode
= channel
->channel
;
1505 struct suspended
*suspended
, *next_suspended
;
1507 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1509 next_suspended
= suspended
->previous
;
1510 free(suspended
->suspender
);
1511 free(suspended
->reason
);
1512 if(suspended
->expires
)
1513 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1518 cNode
->channel_info
= NULL
;
1520 channel
->channel
->channel_info
= NULL
;
1522 dict_delete(channel
->notes
);
1523 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1524 if(!IsSuspended(channel
))
1525 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1526 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1527 UnlockChannel(channel
->channel
);
1529 registered_channels
--;
1533 expire_channels(UNUSED_ARG(void *data
))
1535 struct chanData
*channel
, *next
;
1536 struct userData
*user
;
1537 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1539 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1540 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1542 for(channel
= channelList
; channel
; channel
= next
)
1544 next
= channel
->next
;
1546 /* See if the channel can be expired. */
1547 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1548 || IsProtected(channel
))
1551 /* Make sure there are no high-ranking users still in the channel. */
1552 for(user
=channel
->users
; user
; user
=user
->next
)
1553 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1558 /* Unregister the channel */
1559 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1560 unregister_channel(channel
, "registration expired.");
1563 if(chanserv_conf
.channel_expire_frequency
)
1564 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1568 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1570 char protect
= channel
->chOpts
[chProtect
];
1571 struct userData
*cs_victim
, *cs_aggressor
;
1573 /* Don't protect if no one is to be protected, someone is attacking
1574 himself, or if the aggressor is an IRC Operator. */
1575 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1578 /* Don't protect if the victim isn't authenticated (because they
1579 can't be a channel user), unless we are to protect non-users
1581 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1582 if(protect
!= 'a' && !cs_victim
)
1585 /* Protect if the aggressor isn't a user because at this point,
1586 the aggressor can only be less than or equal to the victim. */
1587 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1591 /* If the aggressor was a user, then the victim can't be helped. */
1598 if(cs_victim
->access
> cs_aggressor
->access
)
1603 if(cs_victim
->access
>= cs_aggressor
->access
)
1612 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1614 struct chanData
*cData
= channel
->channel_info
;
1615 struct userData
*cs_victim
;
1617 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1618 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1619 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1621 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1629 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1631 struct chanData
*cData
= channel
->channel_info
;
1632 struct userData
*cs_victim
;
1634 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1635 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1636 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1638 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1647 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1649 if(IsService(victim
))
1651 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1655 if(protect_user(victim
, user
, channel
->channel_info
))
1657 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1665 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1667 if(IsService(victim
))
1669 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1673 if(protect_user(victim
, user
, channel
->channel_info
))
1675 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1682 static struct do_not_register
*
1683 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1685 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1686 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1687 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1688 strcpy(dnr
->reason
, reason
);
1690 if(dnr
->chan_name
[0] == '*')
1691 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1692 else if(strpbrk(dnr
->chan_name
, "*?"))
1693 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1695 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1699 static struct dnrList
1700 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1702 struct dnrList list
;
1704 struct do_not_register
*dnr
;
1706 dnrList_init(&list
);
1707 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1708 dnrList_append(&list
, dnr
);
1709 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1710 dnrList_append(&list
, dnr
);
1712 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1713 if(match_ircglob(chan_name
, iter_key(it
)))
1714 dnrList_append(&list
, iter_data(it
));
1719 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1721 struct dnrList list
;
1722 struct do_not_register
*dnr
;
1724 char buf
[INTERVALLEN
];
1726 list
= chanserv_find_dnrs(chan_name
, handle
);
1727 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1729 dnr
= list
.list
[ii
];
1732 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1733 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1736 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1739 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1744 struct do_not_register
*
1745 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1747 struct do_not_register
*dnr
;
1750 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1754 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1756 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1757 if(match_ircglob(chan_name
, iter_key(it
)))
1758 return iter_data(it
);
1763 static CHANSERV_FUNC(cmd_noregister
)
1766 struct do_not_register
*dnr
;
1767 char buf
[INTERVALLEN
];
1768 unsigned int matches
;
1774 reply("CSMSG_DNR_SEARCH_RESULTS");
1777 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1779 dnr
= iter_data(it
);
1781 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1783 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1786 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1788 dnr
= iter_data(it
);
1790 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1792 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1795 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1797 dnr
= iter_data(it
);
1799 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1801 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1806 reply("MSG_MATCH_COUNT", matches
);
1808 reply("MSG_NO_MATCHES");
1814 if(!IsChannelName(target
) && (*target
!= '*'))
1816 reply("CSMSG_NOT_DNR", target
);
1822 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1823 if((*target
== '*') && !get_handle_info(target
+ 1))
1825 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1828 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1829 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1833 reply("CSMSG_DNR_SEARCH_RESULTS");
1836 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1838 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1840 reply("MSG_NO_MATCHES");
1844 static CHANSERV_FUNC(cmd_allowregister
)
1846 const char *chan_name
= argv
[1];
1848 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1850 dict_remove(handle_dnrs
, chan_name
+1);
1851 reply("CSMSG_DNR_REMOVED", chan_name
);
1853 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1855 dict_remove(plain_dnrs
, chan_name
);
1856 reply("CSMSG_DNR_REMOVED", chan_name
);
1858 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1860 dict_remove(mask_dnrs
, chan_name
);
1861 reply("CSMSG_DNR_REMOVED", chan_name
);
1865 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1872 chanserv_get_owned_count(struct handle_info
*hi
)
1874 struct userData
*cList
;
1877 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1878 if(cList
->access
== UL_OWNER
)
1883 static CHANSERV_FUNC(cmd_register
)
1885 struct handle_info
*handle
;
1886 struct chanData
*cData
;
1887 struct modeNode
*mn
;
1888 char reason
[MAXLEN
];
1890 unsigned int new_channel
, force
=0;
1891 struct do_not_register
*dnr
;
1897 if(channel
->channel_info
)
1899 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1903 if(channel
->bad_channel
)
1905 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1909 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1911 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1916 chan_name
= channel
->name
;
1922 reply("MSG_MISSING_PARAMS", cmd
->name
);
1923 svccmd_send_help_brief(user
, chanserv
, cmd
);
1926 if(!IsChannelName(argv
[1]))
1928 reply("MSG_NOT_CHANNEL_NAME");
1932 if(opserv_bad_channel(argv
[1]))
1934 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1939 chan_name
= argv
[1];
1942 if(argc
>= (new_channel
+2))
1944 if(!IsHelping(user
))
1946 reply("CSMSG_PROXY_FORBIDDEN");
1950 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1952 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1953 dnr
= chanserv_is_dnr(chan_name
, handle
);
1955 /* Check if they are over the limit.. */
1956 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1958 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1965 handle
= user
->handle_info
;
1966 dnr
= chanserv_is_dnr(chan_name
, handle
);
1967 /* Check if they are over the limit.. */
1968 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1970 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf
.max_owned
);
1973 /* Check if another service is in the channel */
1975 for(n
= 0; n
< channel
->members
.used
; n
++)
1977 mn
= channel
->members
.list
[n
];
1978 if((mn
&& mn
->user
&& (mn
->user
->modes
& FLAGS_SERVICE
)) || IsLocal(mn
->user
))
1980 reply("CSMSG_ANOTHER_SERVICE");
1987 if(!IsHelping(user
))
1988 reply("CSMSG_DNR_CHANNEL", chan_name
);
1990 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1994 /* now handled above for message specilization *
1995 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1997 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2003 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2005 cData
= register_channel(channel
, user
->handle_info
->handle
);
2006 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
2007 cData
->modes
= chanserv_conf
.default_modes
;
2009 cData
->modes
.modes_set
|= MODE_REGISTERED
;
2010 if (IsOffChannel(cData
))
2012 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
2016 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
2017 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
2018 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
2020 mod_chanmode_announce(chanserv
, channel
, change
);
2021 mod_chanmode_free(change
);
2024 /* Initialize the channel's max user record. */
2025 cData
->max
= channel
->members
.used
;
2027 if(handle
!= user
->handle_info
)
2028 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
2030 reply("CSMSG_REG_SUCCESS", channel
->name
);
2032 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
2033 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2038 make_confirmation_string(struct userData
*uData
)
2040 static char strbuf
[16];
2045 for(src
= uData
->handle
->handle
; *src
; )
2046 accum
= accum
* 31 + toupper(*src
++);
2048 for(src
= uData
->channel
->channel
->name
; *src
; )
2049 accum
= accum
* 31 + toupper(*src
++);
2050 sprintf(strbuf
, "%08x", accum
);
2054 static CHANSERV_FUNC(cmd_unregister
)
2057 char reason
[MAXLEN
];
2058 struct chanData
*cData
;
2059 struct userData
*uData
;
2061 cData
= channel
->channel_info
;
2064 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2068 uData
= GetChannelUser(cData
, user
->handle_info
);
2069 if(!uData
|| (uData
->access
< UL_OWNER
))
2071 reply("CSMSG_NO_ACCESS");
2075 if(IsProtected(cData
))
2077 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2081 if(!IsHelping(user
))
2083 const char *confirm_string
;
2084 if(IsSuspended(cData
))
2086 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2089 confirm_string
= make_confirmation_string(uData
);
2090 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2092 reply("CSMSG_CONFIRM_UNREG", channel
->name
, confirm_string
);
2097 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2098 name
= strdup(channel
->name
);
2099 unregister_channel(cData
, reason
);
2100 reply("CSMSG_UNREG_SUCCESS", name
);
2105 static CHANSERV_FUNC(cmd_move
)
2107 struct mod_chanmode change
;
2108 struct chanNode
*target
;
2109 struct modeNode
*mn
;
2110 struct userData
*uData
;
2111 char reason
[MAXLEN
];
2112 struct do_not_register
*dnr
;
2116 if(IsProtected(channel
->channel_info
))
2118 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2122 if(!IsChannelName(argv
[1]))
2124 reply("MSG_NOT_CHANNEL_NAME");
2128 if(opserv_bad_channel(argv
[1]))
2130 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2134 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2136 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2138 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2140 if(!IsHelping(user
))
2141 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2143 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2149 mod_chanmode_init(&change
);
2150 if(!(target
= GetChannel(argv
[1])))
2152 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2153 if(!IsSuspended(channel
->channel_info
))
2154 AddChannelUser(chanserv
, target
);
2156 else if(target
->channel_info
)
2158 reply("CSMSG_ALREADY_REGGED", target
->name
);
2161 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2162 && !IsHelping(user
))
2164 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2167 else if(!IsSuspended(channel
->channel_info
))
2170 change
.args
[0].mode
= MODE_CHANOP
;
2171 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2172 mod_chanmode_announce(chanserv
, target
, &change
);
2177 /* Clear MODE_REGISTERED from old channel, add it to new. */
2179 change
.modes_clear
= MODE_REGISTERED
;
2180 mod_chanmode_announce(chanserv
, channel
, &change
);
2181 change
.modes_clear
= 0;
2182 change
.modes_set
= MODE_REGISTERED
;
2183 mod_chanmode_announce(chanserv
, target
, &change
);
2186 /* Move the channel_info to the target channel; it
2187 shouldn't be necessary to clear timeq callbacks
2188 for the old channel. */
2189 target
->channel_info
= channel
->channel_info
;
2190 target
->channel_info
->channel
= target
;
2191 channel
->channel_info
= NULL
;
2193 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2195 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2196 if(!IsSuspended(target
->channel_info
))
2198 char reason2
[MAXLEN
];
2199 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2200 DelChannelUser(chanserv
, channel
, reason2
, 0);
2202 UnlockChannel(channel
);
2203 LockChannel(target
);
2204 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2209 merge_users(struct chanData
*source
, struct chanData
*target
)
2211 struct userData
*suData
, *tuData
, *next
;
2217 /* Insert the source's users into the scratch area. */
2218 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2219 dict_insert(merge
, suData
->handle
->handle
, suData
);
2221 /* Iterate through the target's users, looking for
2222 users common to both channels. The lower access is
2223 removed from either the scratch area or target user
2225 for(tuData
= target
->users
; tuData
; tuData
= next
)
2227 struct userData
*choice
;
2229 next
= tuData
->next
;
2231 /* If a source user exists with the same handle as a target
2232 channel's user, resolve the conflict by removing one. */
2233 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2237 /* Pick the data we want to keep. */
2238 /* If the access is the same, use the later seen time. */
2239 if(suData
->access
== tuData
->access
)
2240 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2241 else /* Otherwise, keep the higher access level. */
2242 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2244 /* Remove the user that wasn't picked. */
2245 if(choice
== tuData
)
2247 dict_remove(merge
, suData
->handle
->handle
);
2248 del_channel_user(suData
, 0);
2251 del_channel_user(tuData
, 0);
2254 /* Move the remaining users to the target channel. */
2255 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2257 suData
= iter_data(it
);
2259 /* Insert the user into the target channel's linked list. */
2260 suData
->prev
= NULL
;
2261 suData
->next
= target
->users
;
2262 suData
->channel
= target
;
2265 target
->users
->prev
= suData
;
2266 target
->users
= suData
;
2268 /* Update the user counts for the target channel; the
2269 source counts are left alone. */
2270 target
->userCount
++;
2273 /* Possible to assert (source->users == NULL) here. */
2274 source
->users
= NULL
;
2279 merge_bans(struct chanData
*source
, struct chanData
*target
)
2281 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2283 /* Hold on to the original head of the target ban list
2284 to avoid comparing source bans with source bans. */
2285 tFront
= target
->bans
;
2287 /* Perform a totally expensive O(n*m) merge, ick. */
2288 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2290 /* Flag to track whether the ban's been moved
2291 to the destination yet. */
2294 /* Possible to assert (sbData->prev == NULL) here. */
2295 sNext
= sbData
->next
;
2297 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2299 tNext
= tbData
->next
;
2301 /* Perform two comparisons between each source
2302 and target ban, conflicts are resolved by
2303 keeping the broader ban and copying the later
2304 expiration and triggered time. */
2305 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2307 /* There is a broader ban in the target channel that
2308 overrides one in the source channel; remove the
2309 source ban and break. */
2310 if(sbData
->expires
> tbData
->expires
)
2311 tbData
->expires
= sbData
->expires
;
2312 if(sbData
->triggered
> tbData
->triggered
)
2313 tbData
->triggered
= sbData
->triggered
;
2314 del_channel_ban(sbData
);
2317 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2319 /* There is a broader ban in the source channel that
2320 overrides one in the target channel; remove the
2321 target ban, fall through and move the source over. */
2322 if(tbData
->expires
> sbData
->expires
)
2323 sbData
->expires
= tbData
->expires
;
2324 if(tbData
->triggered
> sbData
->triggered
)
2325 sbData
->triggered
= tbData
->triggered
;
2326 if(tbData
== tFront
)
2328 del_channel_ban(tbData
);
2331 /* Source bans can override multiple target bans, so
2332 we allow a source to run through this loop multiple
2333 times, but we can only move it once. */
2338 /* Remove the source ban from the source ban list. */
2340 sbData
->next
->prev
= sbData
->prev
;
2342 /* Modify the source ban's associated channel. */
2343 sbData
->channel
= target
;
2345 /* Insert the ban into the target channel's linked list. */
2346 sbData
->prev
= NULL
;
2347 sbData
->next
= target
->bans
;
2350 target
->bans
->prev
= sbData
;
2351 target
->bans
= sbData
;
2353 /* Update the user counts for the target channel. */
2358 /* Possible to assert (source->bans == NULL) here. */
2359 source
->bans
= NULL
;
2363 merge_data(struct chanData
*source
, struct chanData
*target
)
2365 if(source
->visited
> target
->visited
)
2366 target
->visited
= source
->visited
;
2370 merge_channel(struct chanData
*source
, struct chanData
*target
)
2372 merge_users(source
, target
);
2373 merge_bans(source
, target
);
2374 merge_data(source
, target
);
2377 static CHANSERV_FUNC(cmd_merge
)
2379 struct userData
*target_user
;
2380 struct chanNode
*target
;
2381 char reason
[MAXLEN
];
2385 /* Make sure the target channel exists and is registered to the user
2386 performing the command. */
2387 if(!(target
= GetChannel(argv
[1])))
2389 reply("MSG_INVALID_CHANNEL");
2393 if(!target
->channel_info
)
2395 reply("CSMSG_NOT_REGISTERED", target
->name
);
2399 if(IsProtected(channel
->channel_info
))
2401 reply("CSMSG_MERGE_NODELETE");
2405 if(IsSuspended(target
->channel_info
))
2407 reply("CSMSG_MERGE_SUSPENDED");
2411 if(channel
== target
)
2413 reply("CSMSG_MERGE_SELF");
2417 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2418 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2420 reply("CSMSG_MERGE_NOT_OWNER");
2424 /* Merge the channel structures and associated data. */
2425 merge_channel(channel
->channel_info
, target
->channel_info
);
2426 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2427 unregister_channel(channel
->channel_info
, reason
);
2428 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2432 static CHANSERV_FUNC(cmd_opchan
)
2434 struct mod_chanmode change
;
2435 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2437 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2440 channel
->channel_info
->may_opchan
= 0;
2441 mod_chanmode_init(&change
);
2443 change
.args
[0].mode
= MODE_CHANOP
;
2444 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2445 mod_chanmode_announce(chanserv
, channel
, &change
);
2446 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2450 static CHANSERV_FUNC(cmd_adduser
)
2452 struct userData
*actee
;
2453 struct userData
*actor
;
2454 struct handle_info
*handle
;
2455 unsigned short access
;
2459 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2461 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2465 access
= user_level_from_name(argv
[2], UL_OWNER
);
2468 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2472 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2473 if(actor
->access
<= access
)
2475 reply("CSMSG_NO_BUMP_ACCESS");
2479 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2481 // 'kevin must first authenticate with AuthServ.' is sent to user
2482 struct userNode
*unode
;
2483 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2486 if(find_adduser_pending(channel
, unode
)) {
2487 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2490 if(IsInChannel(channel
, unode
)) {
2491 reply("CSMSG_ADDUSER_PENDING");
2492 add_adduser_pending(channel
, unode
, access
);
2493 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2495 /* this results in user must auth AND not in chan errors. too confusing..
2497 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2505 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2507 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2511 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2512 scan_user_presence(actee
, NULL
);
2513 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2517 static CHANSERV_FUNC(cmd_clvl
)
2519 struct handle_info
*handle
;
2520 struct userData
*victim
;
2521 struct userData
*actor
;
2522 unsigned short new_access
;
2523 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2527 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2529 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2532 if(handle
== user
->handle_info
&& !privileged
)
2534 reply("CSMSG_NO_SELF_CLVL");
2538 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2540 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2544 if(actor
->access
<= victim
->access
&& !privileged
)
2546 reply("MSG_USER_OUTRANKED", handle
->handle
);
2550 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2554 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2558 if(new_access
>= actor
->access
&& !privileged
)
2560 reply("CSMSG_NO_BUMP_ACCESS");
2564 victim
->access
= new_access
;
2565 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2569 static CHANSERV_FUNC(cmd_deluser
)
2571 struct handle_info
*handle
;
2572 struct userData
*victim
;
2573 struct userData
*actor
;
2574 unsigned short access
;
2579 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2581 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2584 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2586 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2592 access
= user_level_from_name(argv
[1], UL_OWNER
);
2595 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2598 if(access
!= victim
->access
)
2600 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2606 access
= victim
->access
;
2609 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2611 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2615 chan_name
= strdup(channel
->name
);
2616 del_channel_user(victim
, 1);
2617 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2623 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2625 struct userData
*actor
, *uData
, *next
;
2627 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2629 if(min_access
> max_access
)
2631 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2635 if((actor
->access
<= max_access
) && !IsHelping(user
))
2637 reply("CSMSG_NO_ACCESS");
2641 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2645 if((uData
->access
>= min_access
)
2646 && (uData
->access
<= max_access
)
2647 && match_ircglob(uData
->handle
->handle
, mask
))
2648 del_channel_user(uData
, 1);
2651 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2655 static CHANSERV_FUNC(cmd_mdelowner
)
2657 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2660 static CHANSERV_FUNC(cmd_mdelcoowner
)
2662 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2665 static CHANSERV_FUNC(cmd_mdelmanager
)
2667 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2670 static CHANSERV_FUNC(cmd_mdelop
)
2672 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2675 static CHANSERV_FUNC(cmd_mdelpeon
)
2677 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2680 static CHANSERV_FUNC(cmd_mdelhalfop
)
2682 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2688 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2690 struct banData
*bData
, *next
;
2691 char interval
[INTERVALLEN
];
2696 limit
= now
- duration
;
2697 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2701 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2704 del_channel_ban(bData
);
2708 intervalString(interval
, duration
, user
->handle_info
);
2709 send_message(user
, chanserv
, "CSMSG_TRIMMED_LAMERS", count
, channel
->name
, interval
);
2714 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2716 struct userData
*actor
, *uData
, *next
;
2717 char interval
[INTERVALLEN
];
2721 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2722 if(min_access
> max_access
)
2724 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2728 if((actor
->access
<= max_access
) && !IsHelping(user
))
2730 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2735 limit
= now
- duration
;
2736 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2740 if((uData
->seen
> limit
) || uData
->present
)
2743 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2744 || (!max_access
&& (uData
->access
< actor
->access
)))
2746 del_channel_user(uData
, 1);
2754 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2756 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2760 static CHANSERV_FUNC(cmd_trim
)
2762 unsigned long duration
;
2763 unsigned short min_level
, max_level
;
2767 duration
= ParseInterval(argv
[2]);
2770 reply("CSMSG_CANNOT_TRIM");
2774 if(!irccasecmp(argv
[1], "lamers"))
2776 cmd_trim_bans(user
, channel
, duration
); /* trim_lamers.. */
2779 else if(!irccasecmp(argv
[1], "users"))
2781 cmd_trim_users(user
, channel
, 0, 0, duration
);
2784 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2786 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2789 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2791 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2796 reply("CSMSG_INVALID_TRIM", argv
[1]);
2801 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2802 to the user. cmd_all takes advantage of this. */
2803 static CHANSERV_FUNC(cmd_up
)
2805 struct mod_chanmode change
;
2806 struct userData
*uData
;
2809 mod_chanmode_init(&change
);
2811 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2812 if(!change
.args
[0].u
.member
)
2815 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2819 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2823 reply("CSMSG_GODMODE_UP", argv
[0]);
2826 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2828 change
.args
[0].mode
= MODE_CHANOP
;
2829 errmsg
= "CSMSG_ALREADY_OPPED";
2831 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2833 change
.args
[0].mode
= MODE_HALFOP
;
2834 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2836 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chVoice
] == 'p' || channel
->channel_info
->chOpts
[chVoice
] == 'a'))
2838 change
.args
[0].mode
= MODE_VOICE
;
2839 errmsg
= "CSMSG_ALREADY_VOICED";
2844 reply("CSMSG_NO_ACCESS");
2847 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2848 if(!change
.args
[0].mode
)
2851 reply(errmsg
, channel
->name
);
2854 modcmd_chanmode_announce(&change
);
2858 static CHANSERV_FUNC(cmd_down
)
2860 struct mod_chanmode change
;
2862 mod_chanmode_init(&change
);
2864 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2865 if(!change
.args
[0].u
.member
)
2868 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2872 if(!change
.args
[0].u
.member
->modes
)
2875 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2879 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2880 modcmd_chanmode_announce(&change
);
2884 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
)
2886 struct userData
*cList
;
2888 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2890 if(IsSuspended(cList
->channel
)
2891 || IsUserSuspended(cList
)
2892 || !GetUserMode(cList
->channel
->channel
, user
))
2895 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2901 static CHANSERV_FUNC(cmd_upall
)
2903 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2906 static CHANSERV_FUNC(cmd_downall
)
2908 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2911 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2912 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2915 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
)
2917 unsigned int ii
, valid
;
2918 struct userNode
*victim
;
2919 struct mod_chanmode
*change
;
2921 change
= mod_chanmode_alloc(argc
- 1);
2923 for(ii
=valid
=0; ++ii
< argc
; )
2925 if(!(victim
= GetUserH(argv
[ii
])))
2927 change
->args
[valid
].mode
= mode
;
2928 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2929 if(!change
->args
[valid
].u
.member
)
2931 if(validate
&& !validate(user
, channel
, victim
))
2936 change
->argc
= valid
;
2937 if(valid
< (argc
-1))
2938 reply("CSMSG_PROCESS_FAILED");
2941 modcmd_chanmode_announce(change
);
2942 reply(action
, channel
->name
);
2944 mod_chanmode_free(change
);
2948 static CHANSERV_FUNC(cmd_op
)
2950 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2953 static CHANSERV_FUNC(cmd_hop
)
2955 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2958 static CHANSERV_FUNC(cmd_deop
)
2960 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2963 static CHANSERV_FUNC(cmd_dehop
)
2965 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2968 static CHANSERV_FUNC(cmd_voice
)
2970 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2973 static CHANSERV_FUNC(cmd_devoice
)
2975 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2979 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2985 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2987 struct modeNode
*mn
= channel
->members
.list
[ii
];
2989 if(IsService(mn
->user
))
2992 if(!user_matches_glob(mn
->user
, ban
, 1))
2995 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2999 victims
[(*victimCount
)++] = mn
;
3005 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3007 struct userNode
*victim
;
3008 struct modeNode
**victims
;
3009 unsigned int offset
, n
, victimCount
, duration
= 0;
3010 char *reason
= "Bye.", *ban
, *name
;
3011 char interval
[INTERVALLEN
];
3013 offset
= (action
& ACTION_ADD_TIMED_LAMER
) ? 3 : 2;
3014 REQUIRE_PARAMS(offset
);
3017 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
3018 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
3020 /* Truncate the reason to a length of TOPICLEN, as
3021 the ircd does; however, leave room for an ellipsis
3022 and the kicker's nick. */
3023 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
3027 if((victim
= GetUserH(argv
[1])))
3029 victims
= alloca(sizeof(victims
[0]));
3030 victims
[0] = GetUserMode(channel
, victim
);
3031 /* XXX: The comparison with ACTION_KICK is just because all
3032 * other actions can work on users outside the channel, and we
3033 * want to allow those (e.g. unbans) in that case. If we add
3034 * some other ejection action for in-channel users, change
3036 victimCount
= victims
[0] ? 1 : 0;
3038 if(IsService(victim
))
3040 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3044 if((action
== ACTION_KICK
) && !victimCount
)
3046 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3050 if(protect_user(victim
, user
, channel
->channel_info
))
3052 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3056 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3057 name
= victim
->nick
;
3061 if(!is_ircmask(argv
[1]))
3063 reply("MSG_NICK_UNKNOWN", argv
[1]);
3067 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3069 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3071 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3074 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3076 victimCount
= 0; /* Dont deop etc ppl who match this */
3078 #ifdef entropy_lameness
3079 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
3081 reply("CSMSG_LAME_MASK", argv
[1]);
3086 if((action
== ACTION_KICK
) && (victimCount
== 0))
3088 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3092 name
= ban
= strdup(argv
[1]);
3095 /* Truncate the ban in place if necessary; we must ensure
3096 that 'ban' is a valid ban mask before sanitizing it. */
3097 sanitize_ircmask(ban
);
3099 if(action
& ACTION_ADD_LAMER
)
3101 struct banData
*bData
, *next
;
3103 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
) /* ..lamers.. */
3105 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf
.max_chan_bans
); /* ..lamers.. */
3110 if(action
& ACTION_ADD_TIMED_LAMER
)
3112 duration
= ParseInterval(argv
[2]);
3116 reply("CSMSG_DURATION_TOO_LOW");
3120 else if(duration
> (86400 * 365 * 2))
3122 reply("CSMSG_DURATION_TOO_HIGH");
3129 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3131 if(match_ircglobs(bData
->mask
, ban
))
3133 int exact
= !irccasecmp(bData
->mask
, ban
);
3135 /* The ban is redundant; there is already a ban
3136 with the same effect in place. */
3140 free(bData
->reason
);
3141 bData
->reason
= strdup(reason
);
3142 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3144 reply("CSMSG_REASON_CHANGE", ban
);
3148 if(exact
&& bData
->expires
)
3152 /* If the ban matches an existing one exactly,
3153 extend the expiration time if the provided
3154 duration is longer. */
3155 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3157 bData
->expires
= now
+ duration
;
3168 /* Delete the expiration timeq entry and
3169 requeue if necessary. */
3170 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3173 timeq_add(bData
->expires
, expire_ban
, bData
);
3177 /* automated kickban */
3180 reply("CSMSG_LAMER_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3182 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3188 reply("CSMSG_REDUNDANT_LAMER", name
, channel
->name
);
3195 if(match_ircglobs(ban
, bData
->mask
))
3197 /* The ban we are adding makes previously existing
3198 bans redundant; silently remove them. */
3199 del_channel_ban(bData
);
3203 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
);
3205 name
= ban
= strdup(bData
->mask
);
3209 /* WHAT DOES THIS DO?? -Rubin */
3210 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3212 extern const char *hidden_host_suffix
;
3213 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3215 unsigned int l1
, l2
;
3218 l2
= strlen(old_name
);
3221 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3223 new_mask
= malloc(MAXLEN
);
3224 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3226 name
= ban
= new_mask
;
3231 if(action
& ACTION_BAN
)
3233 unsigned int exists
;
3234 struct mod_chanmode
*change
;
3236 if(channel
->banlist
.used
>= MAXBANS
)
3239 reply("CSMSG_BANLIST_FULL", channel
->name
);
3244 exists
= ChannelBanExists(channel
, ban
);
3245 change
= mod_chanmode_alloc(victimCount
+ 1);
3246 for(n
= 0; n
< victimCount
; ++n
)
3248 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3249 change
->args
[n
].u
.member
= victims
[n
];
3253 change
->args
[n
].mode
= MODE_BAN
;
3254 change
->args
[n
++].u
.hostmask
= ban
;
3258 modcmd_chanmode_announce(change
);
3260 mod_chanmode_announce(chanserv
, channel
, change
);
3261 mod_chanmode_free(change
);
3263 if(exists
&& (action
== ACTION_BAN
))
3266 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3272 if(action
& ACTION_KICK
)
3274 char kick_reason
[MAXLEN
];
3275 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3277 for(n
= 0; n
< victimCount
; n
++)
3278 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3283 /* No response, since it was automated. */
3285 else if(action
& ACTION_ADD_LAMER
)
3288 reply("CSMSG_TIMED_LAMER_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3290 reply("CSMSG_LAMER_ADDED", name
, channel
->name
);
3292 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3293 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3294 else if(action
& ACTION_BAN
)
3295 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3296 else if(action
& ACTION_KICK
&& victimCount
)
3297 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3303 static CHANSERV_FUNC(cmd_kickban
)
3305 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3308 static CHANSERV_FUNC(cmd_kick
)
3310 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3313 static CHANSERV_FUNC(cmd_ban
)
3315 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3318 static CHANSERV_FUNC(cmd_addlamer
)
3320 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
);
3323 static CHANSERV_FUNC(cmd_addtimedlamer
)
3325 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_LAMER
| ACTION_ADD_TIMED_LAMER
);
3328 static struct mod_chanmode
*
3329 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3331 struct mod_chanmode
*change
;
3332 unsigned char *match
;
3333 unsigned int ii
, count
;
3335 match
= alloca(bans
->used
);
3338 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3340 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3347 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3349 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3356 change
= mod_chanmode_alloc(count
);
3357 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3361 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3362 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3364 assert(count
== change
->argc
);
3369 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3371 struct userNode
*actee
;
3377 /* may want to allow a comma delimited list of users... */
3378 if(!(actee
= GetUserH(argv
[1])))
3380 if(!is_ircmask(argv
[1]))
3382 reply("MSG_NICK_UNKNOWN", argv
[1]);
3386 mask
= strdup(argv
[1]);
3389 /* We don't sanitize the mask here because ircu
3391 if(action
& ACTION_UNBAN
)
3393 struct mod_chanmode
*change
;
3394 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3399 modcmd_chanmode_announce(change
);
3400 for(ii
= 0; ii
< change
->argc
; ++ii
)
3401 free((char*)change
->args
[ii
].u
.hostmask
);
3402 mod_chanmode_free(change
);
3407 if(action
& ACTION_DEL_LAMER
)
3409 struct banData
*ban
, *next
;
3411 ban
= channel
->channel_info
->bans
; /* lamers */
3415 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3418 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3423 del_channel_ban(ban
);
3430 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3432 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3438 static CHANSERV_FUNC(cmd_unban
)
3440 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3443 static CHANSERV_FUNC(cmd_dellamer
)
3445 /* it doesn't necessarily have to remove the channel ban - may want
3446 to make that an option. */
3447 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_LAMER
);
3450 static CHANSERV_FUNC(cmd_unbanme
)
3452 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3453 long flags
= ACTION_UNBAN
;
3455 /* remove permanent bans if the user has the proper access. */
3456 if(uData
->access
>= UL_MANAGER
)
3457 flags
|= ACTION_DEL_LAMER
;
3459 argv
[1] = user
->nick
;
3460 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3463 static CHANSERV_FUNC(cmd_unbanall
)
3465 struct mod_chanmode
*change
;
3468 if(!channel
->banlist
.used
)
3470 reply("CSMSG_NO_BANS", channel
->name
);
3474 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3475 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3477 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3478 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3480 modcmd_chanmode_announce(change
);
3481 for(ii
= 0; ii
< change
->argc
; ++ii
)
3482 free((char*)change
->args
[ii
].u
.hostmask
);
3483 mod_chanmode_free(change
);
3484 reply("CSMSG_BANS_REMOVED", channel
->name
);
3488 static CHANSERV_FUNC(cmd_open
)
3490 struct mod_chanmode
*change
;
3493 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3495 change
= mod_chanmode_alloc(0);
3496 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3497 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3498 && channel
->channel_info
->modes
.modes_set
)
3499 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3500 modcmd_chanmode_announce(change
);
3501 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3502 for(ii
= 0; ii
< change
->argc
; ++ii
)
3503 free((char*)change
->args
[ii
].u
.hostmask
);
3504 mod_chanmode_free(change
);
3508 static CHANSERV_FUNC(cmd_myaccess
)
3510 static struct string_buffer sbuf
;
3511 struct handle_info
*target_handle
;
3512 struct userData
*uData
;
3515 target_handle
= user
->handle_info
;
3516 else if(!IsHelping(user
))
3518 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3521 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3524 if(!target_handle
->channels
)
3526 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3530 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3531 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3533 struct chanData
*cData
= uData
->channel
;
3535 if(uData
->access
> UL_OWNER
)
3537 if(IsProtected(cData
)
3538 && (target_handle
!= user
->handle_info
)
3539 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3542 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3543 if(uData
->flags
== USER_AUTO_OP
)
3544 string_buffer_append(&sbuf
, ',');
3545 if(IsUserSuspended(uData
))
3546 string_buffer_append(&sbuf
, 's');
3547 if(IsUserAutoOp(uData
))
3549 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3550 string_buffer_append(&sbuf
, 'o');
3551 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3552 string_buffer_append(&sbuf
, 'h');
3553 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3554 string_buffer_append(&sbuf
, 'v');
3556 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3557 string_buffer_append(&sbuf
, 'i');
3559 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3561 string_buffer_append_string(&sbuf
, ")]");
3562 string_buffer_append(&sbuf
, '\0');
3563 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3569 static CHANSERV_FUNC(cmd_access
)
3571 struct userNode
*target
;
3572 struct handle_info
*target_handle
;
3573 struct userData
*uData
;
3575 char prefix
[MAXLEN
];
3580 target_handle
= target
->handle_info
;
3582 else if((target
= GetUserH(argv
[1])))
3584 target_handle
= target
->handle_info
;
3586 else if(argv
[1][0] == '*')
3588 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3590 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3596 reply("MSG_NICK_UNKNOWN", argv
[1]);
3600 assert(target
|| target_handle
);
3602 if(target
== chanserv
)
3604 reply("CSMSG_IS_CHANSERV");
3612 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3617 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3620 reply("MSG_AUTHENTICATE");
3626 const char *epithet
= NULL
, *type
= NULL
;
3629 epithet
= chanserv_conf
.irc_operator_epithet
;
3632 else if(IsNetworkHelper(target
))
3634 epithet
= chanserv_conf
.network_helper_epithet
;
3635 type
= "network helper";
3637 else if(IsSupportHelper(target
))
3639 epithet
= chanserv_conf
.support_helper_epithet
;
3640 type
= "support helper";
3644 if(target_handle
->epithet
)
3645 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3647 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3649 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3653 sprintf(prefix
, "%s", target_handle
->handle
);
3656 if(!channel
->channel_info
)
3658 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3662 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3663 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3664 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3666 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3667 /* To prevent possible information leaks, only show infolines
3668 * if the requestor is in the channel or it's their own
3670 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3672 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3674 /* Likewise, only say it's suspended if the user has active
3675 * access in that channel or it's their own entry. */
3676 if(IsUserSuspended(uData
)
3677 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3678 || (user
->handle_info
== uData
->handle
)))
3680 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3685 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3691 /* This is never used... */
3693 zoot_list(struct listData
*list
)
3695 struct userData
*uData
;
3696 unsigned int start
, curr
, highest
, lowest
;
3697 struct helpfile_table tmp_table
;
3698 const char **temp
, *msg
;
3700 if(list
->table
.length
== 1)
3703 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
);
3705 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
));
3706 msg
= user_find_message(list
->user
, "MSG_NONE");
3707 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3709 tmp_table
.width
= list
->table
.width
;
3710 tmp_table
.flags
= list
->table
.flags
;
3711 list
->table
.contents
[0][0] = " ";
3712 highest
= list
->highest
;
3713 if(list
->lowest
!= 0)
3714 lowest
= list
->lowest
;
3715 else if(highest
< 100)
3718 lowest
= highest
- 100;
3719 for(start
= curr
= 1; curr
< list
->table
.length
; )
3721 uData
= list
->users
[curr
-1];
3722 list
->table
.contents
[curr
++][0] = " ";
3723 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3726 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
);
3728 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
));
3729 temp
= list
->table
.contents
[--start
];
3730 list
->table
.contents
[start
] = list
->table
.contents
[0];
3731 tmp_table
.contents
= list
->table
.contents
+ start
;
3732 tmp_table
.length
= curr
- start
;
3733 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3734 list
->table
.contents
[start
] = temp
;
3736 highest
= lowest
- 1;
3737 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3743 def_list(struct listData
*list
)
3747 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
);
3749 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
));
3750 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3751 if(list
->table
.length
== 1)
3753 msg
= user_find_message(list
->user
, "MSG_NONE");
3754 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3759 userData_access_comp(const void *arg_a
, const void *arg_b
)
3761 const struct userData
*a
= *(struct userData
**)arg_a
;
3762 const struct userData
*b
= *(struct userData
**)arg_b
;
3764 if(a
->access
!= b
->access
)
3765 res
= b
->access
- a
->access
;
3767 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3772 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3774 void (*send_list
)(struct listData
*);
3775 struct userData
*uData
;
3776 struct listData lData
;
3777 unsigned int matches
;
3781 lData
.bot
= cmd
->parent
->bot
;
3782 lData
.channel
= channel
;
3783 lData
.lowest
= lowest
;
3784 lData
.highest
= highest
;
3785 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3786 send_list
= def_list
;
3787 /* What does the following line do exactly?? */
3788 (void)zoot_list
; /* since it doesn't show user levels */
3790 /* this does nothing!! -rubin
3791 if(user->handle_info)
3793 switch(user->handle_info->userlist_style)
3795 case HI_STYLE_DEF: send_list = def_list; break;
3796 case HI_STYLE_ZOOT: send_list = def_list; break;
3801 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3803 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3805 if((uData
->access
< lowest
)
3806 || (uData
->access
> highest
)
3807 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3809 lData
.users
[matches
++] = uData
;
3811 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3813 lData
.table
.length
= matches
+1;
3814 lData
.table
.width
= 5;
3815 lData
.table
.flags
= TABLE_NO_FREE
;
3816 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3817 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3818 lData
.table
.contents
[0] = ary
;
3822 ary
[3] = "Last Seen";
3824 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3826 struct userData
*uData
= lData
.users
[matches
-1];
3827 char seen
[INTERVALLEN
];
3829 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3830 lData
.table
.contents
[matches
] = ary
;
3831 /* ary[0] = strtab(uData->access);*/
3832 ary
[0] = user_level_name_from_level(uData
->access
);
3833 ary
[1] = strtab(uData
->access
);
3834 ary
[2] = uData
->handle
->handle
;
3837 else if(!uData
->seen
)
3840 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3841 ary
[3] = strdup(ary
[3]);
3842 if(IsUserSuspended(uData
))
3843 ary
[4] = "Suspended";
3844 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3845 ary
[4] = "Vacation";
3850 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3852 free((char*)lData
.table
.contents
[matches
][3]);
3853 free(lData
.table
.contents
[matches
]);
3855 free(lData
.table
.contents
[0]);
3856 free(lData
.table
.contents
);
3860 /* Remove this now that debugging is over? or improve it for
3861 * users? Would it be better tied into USERS somehow? -Rubin */
3862 static CHANSERV_FUNC(cmd_pending
)
3864 struct adduserPending
*ap
;
3865 reply("CSMSG_ADDUSER_PENDING_HEADER");
3867 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3868 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3869 reply("CSMSG_ADDUSER_PENDING_FOOTER");
3873 static CHANSERV_FUNC(cmd_users
)
3875 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3878 static CHANSERV_FUNC(cmd_wlist
)
3880 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3883 static CHANSERV_FUNC(cmd_clist
)
3885 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3888 static CHANSERV_FUNC(cmd_mlist
)
3890 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3893 static CHANSERV_FUNC(cmd_olist
)
3895 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3898 static CHANSERV_FUNC(cmd_hlist
)
3900 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3903 static CHANSERV_FUNC(cmd_plist
)
3905 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3908 static CHANSERV_FUNC(cmd_lamers
)
3910 struct helpfile_table tbl
;
3911 unsigned int matches
= 0, timed
= 0, ii
;
3912 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3913 const char *msg_never
, *triggered
, *expires
;
3914 struct banData
*ban
, **bans
; /* lamers */
3921 reply("CSMSG_LAMERS_HEADER", channel
->name
);
3922 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*)); /* lamers */
3925 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3927 if(search
&& !match_ircglobs(search
, ban
->mask
))
3929 bans
[matches
++] = ban
;
3934 tbl
.length
= matches
+ 1;
3935 tbl
.width
= 4 + timed
;
3937 tbl
.flags
= TABLE_NO_FREE
;
3938 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3939 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3940 tbl
.contents
[0][0] = "Mask";
3941 tbl
.contents
[0][1] = "Set By";
3942 tbl
.contents
[0][2] = "Triggered";
3945 tbl
.contents
[0][3] = "Expires";
3946 tbl
.contents
[0][4] = "Reason";
3949 tbl
.contents
[0][3] = "Reason";
3952 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3954 free(tbl
.contents
[0]);
3959 msg_never
= user_find_message(user
, "MSG_NEVER");
3960 for(ii
= 0; ii
< matches
; )
3966 else if(ban
->expires
)
3967 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3969 expires
= msg_never
;
3972 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3974 triggered
= msg_never
;
3976 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3977 tbl
.contents
[ii
][0] = ban
->mask
;
3978 tbl
.contents
[ii
][1] = ban
->owner
;
3979 tbl
.contents
[ii
][2] = strdup(triggered
);
3982 tbl
.contents
[ii
][3] = strdup(expires
);
3983 tbl
.contents
[ii
][4] = ban
->reason
;
3986 tbl
.contents
[ii
][3] = ban
->reason
;
3988 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3989 /* reply("MSG_MATCH_COUNT", matches); */
3990 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3992 free((char*)tbl
.contents
[ii
][2]);
3994 free((char*)tbl
.contents
[ii
][3]);
3995 free(tbl
.contents
[ii
]);
3997 free(tbl
.contents
[0]);
4004 * return + if the user does NOT have the right to set the topic, and
4005 * the topic is changed.
4008 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
4010 struct chanData
*cData
= channel
->channel_info
;
4011 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4013 else if(cData
->topic
)
4014 return irccasecmp(new_topic
, cData
->topic
);
4021 * Makes a givin topic fit into a givin topic mask and returns
4024 * topic_mask - the mask to conform to
4025 * topic - the topic to make conform
4026 * new_topic - the pre-allocated char* to put the new topic into
4028 * modifies: new_topic
4031 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
4033 //char *topic_mask = cData->topic_mask;
4035 int pos
=0, starpos
=-1, dpos
=0, len
;
4037 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
4044 strcpy(new_topic
, "");
4047 len
= strlen(topic
);
4048 if((dpos
+ len
) > TOPICLEN
)
4049 len
= TOPICLEN
+ 1 - dpos
;
4050 memcpy(new_topic
+dpos
, topic
, len
);
4054 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4055 default: new_topic
[dpos
++] = tchar
; break;
4058 if((dpos
> TOPICLEN
) || tchar
)
4060 strcpy(new_topic
, "");
4063 new_topic
[dpos
] = 0;
4067 static CHANSERV_FUNC(cmd_topic
)
4069 struct chanData
*cData
;
4072 cData
= channel
->channel_info
;
4077 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4078 reply("CSMSG_TOPIC_SET", cData
->topic
);
4082 reply("CSMSG_NO_TOPIC", channel
->name
);
4086 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4087 /* If they say "!topic *", use an empty topic. */
4088 if((topic
[0] == '*') && (topic
[1] == 0))
4091 if(bad_topic(channel
, user
, topic
))
4093 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4098 /* If there is a topicmask set, and the new topic doesnt match, make it */
4099 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4101 char *topic_mask
= cData
->topic_mask
;
4102 char new_topic
[TOPICLEN
+1];
4104 /* make a new topic fitting mask */
4105 conform_topic(topic_mask
, topic
, new_topic
);
4108 /* Topic couldnt fit into mask, was too long */
4109 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4110 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4113 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4115 else /* No mask set, just set the topic */
4116 SetChannelTopic(channel
, chanserv
, topic
, 1);
4119 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4121 /* Grab the topic and save it as the default topic. */
4123 cData
->topic
= strdup(channel
->topic
);
4129 static CHANSERV_FUNC(cmd_mode
)
4131 struct mod_chanmode
*change
;
4135 change
= &channel
->channel_info
->modes
;
4136 if(change
->modes_set
|| change
->modes_clear
) {
4137 modcmd_chanmode_announce(change
);
4138 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4140 reply("CSMSG_NO_MODES", channel
->name
);
4144 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4147 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4151 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4152 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4155 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4156 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4160 modcmd_chanmode_announce(change
);
4161 mod_chanmode_free(change
);
4162 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4166 static CHANSERV_FUNC(cmd_invite
)
4168 struct userData
*uData
;
4169 struct userNode
*invite
;
4171 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4175 if(!(invite
= GetUserH(argv
[1])))
4177 reply("MSG_NICK_UNKNOWN", argv
[1]);
4184 if(GetUserMode(channel
, invite
))
4186 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4194 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4195 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4198 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4200 irc_invite(chanserv
, invite
, channel
);
4202 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4207 static CHANSERV_FUNC(cmd_inviteme
)
4209 if(GetUserMode(channel
, user
))
4211 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4214 if(channel
->channel_info
4215 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4217 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4220 irc_invite(cmd
->parent
->bot
, user
, channel
);
4225 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4228 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4230 /* We display things based on two dimensions:
4231 * - Issue time: present or absent
4232 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4233 * (in order of precedence, so something both expired and revoked
4234 * only counts as revoked)
4236 combo
= (suspended
->issued
? 4 : 0)
4237 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4239 case 0: /* no issue time, indefinite expiration */
4240 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4242 case 1: /* no issue time, expires in future */
4243 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4244 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4246 case 2: /* no issue time, expired */
4247 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4248 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4250 case 3: /* no issue time, revoked */
4251 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4252 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4254 case 4: /* issue time set, indefinite expiration */
4255 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4256 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4258 case 5: /* issue time set, expires in future */
4259 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4260 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4261 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4263 case 6: /* issue time set, expired */
4264 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4265 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4266 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4268 case 7: /* issue time set, revoked */
4269 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4270 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4271 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4274 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4279 static CHANSERV_FUNC(cmd_info
)
4281 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4282 struct userData
*uData
, *owner
;
4283 struct chanData
*cData
;
4284 struct do_not_register
*dnr
;
4289 cData
= channel
->channel_info
;
4290 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4293 uData
= GetChannelUser(cData
, user
->handle_info
);
4294 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4296 mod_chanmode_format(&cData
->modes
, modes
);
4297 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4298 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4301 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4305 note
= iter_data(it
);
4306 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4309 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4310 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4313 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4314 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4315 if(owner
->access
== UL_OWNER
)
4316 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4317 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4318 reply("CSMSG_CHANNEL_LAMERS", cData
->banCount
);
4319 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4320 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4322 privileged
= IsStaff(user
);
4323 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4324 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4326 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4327 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4329 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4331 struct suspended
*suspended
;
4332 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4333 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4334 show_suspension_info(cmd
, user
, suspended
);
4336 else if(IsSuspended(cData
))
4338 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4339 show_suspension_info(cmd
, user
, cData
->suspended
);
4341 reply("CSMSG_CHANNEL_END");
4345 static CHANSERV_FUNC(cmd_netinfo
)
4347 extern time_t boot_time
;
4348 extern unsigned long burst_length
;
4349 char interval
[INTERVALLEN
];
4351 reply("CSMSG_NETWORK_INFO");
4352 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4353 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4354 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4355 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4356 reply("CSMSG_NETWORK_LAMERS", banCount
);
4357 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4358 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4359 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4364 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4366 struct helpfile_table table
;
4368 struct userNode
*user
;
4373 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4374 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4375 for(nn
=0; nn
<list
->used
; nn
++)
4377 user
= list
->list
[nn
];
4378 if(user
->modes
& skip_flags
)
4382 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4385 nick
= alloca(strlen(user
->nick
)+3);
4386 sprintf(nick
, "(%s)", user
->nick
);
4390 table
.contents
[table
.length
][0] = nick
;
4393 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4396 static CHANSERV_FUNC(cmd_ircops
)
4398 reply("CSMSG_STAFF_OPERS");
4399 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4403 static CHANSERV_FUNC(cmd_helpers
)
4405 reply("CSMSG_STAFF_HELPERS");
4406 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4410 static CHANSERV_FUNC(cmd_staff
)
4412 reply("CSMSG_NETWORK_STAFF");
4413 cmd_ircops(CSFUNC_ARGS
);
4414 cmd_helpers(CSFUNC_ARGS
);
4418 static CHANSERV_FUNC(cmd_peek
)
4420 struct modeNode
*mn
;
4421 char modes
[MODELEN
];
4423 struct helpfile_table table
;
4425 irc_make_chanmode(channel
, modes
);
4427 reply("CSMSG_PEEK_INFO", channel
->name
);
4429 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4430 reply("CSMSG_PEEK_MODES", modes
);
4431 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4435 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4436 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4437 for(n
= 0; n
< channel
->members
.used
; n
++)
4439 mn
= channel
->members
.list
[n
];
4440 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4442 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4443 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4448 reply("CSMSG_PEEK_OPS");
4449 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4452 reply("CSMSG_PEEK_NO_OPS");
4453 reply("CSMSG_PEEK_END");
4457 static MODCMD_FUNC(cmd_wipeinfo
)
4459 struct handle_info
*victim
;
4460 struct userData
*ud
, *actor
;
4463 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4464 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4466 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4468 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4471 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4473 reply("MSG_USER_OUTRANKED", victim
->handle
);
4479 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4483 static CHANSERV_FUNC(cmd_resync
)
4485 struct mod_chanmode
*changes
;
4486 struct chanData
*cData
= channel
->channel_info
;
4487 unsigned int ii
, used
;
4489 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4490 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4492 struct modeNode
*mn
= channel
->members
.list
[ii
];
4493 struct userData
*uData
;
4495 if(IsService(mn
->user
))
4498 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4499 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4501 if(!(mn
->modes
& MODE_CHANOP
))
4503 changes
->args
[used
].mode
= MODE_CHANOP
;
4504 changes
->args
[used
++].u
.member
= mn
;
4507 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4509 if(mn
->modes
& MODE_CHANOP
)
4511 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4512 changes
->args
[used
++].u
.member
= mn
;
4514 if(!(mn
->modes
& MODE_HALFOP
))
4516 changes
->args
[used
].mode
= MODE_HALFOP
;
4517 changes
->args
[used
++].u
.member
= mn
;
4519 /* why cant halfops keep voice
4520 if(mn->modes & MODE_VOICE)
4522 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4523 changes->args[used++].u.member = mn;
4527 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4529 if(mn
->modes
& MODE_CHANOP
)
4531 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_CHANOP
;
4532 changes
->args
[used
++].u
.member
= mn
;
4534 if(mn
->modes
& MODE_HALFOP
)
4536 changes
->args
[used
].mode
= MODE_REMOVE
| MODE_HALFOP
;
4537 changes
->args
[used
++].u
.member
= mn
;
4539 if(!(mn
->modes
& MODE_VOICE
))
4541 changes
->args
[used
].mode
= MODE_VOICE
;
4542 changes
->args
[used
++].u
.member
= mn
;
4549 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4550 changes
->args
[used
++].u
.member
= mn
;
4554 changes
->argc
= used
;
4555 modcmd_chanmode_announce(changes
);
4556 mod_chanmode_free(changes
);
4557 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4561 static CHANSERV_FUNC(cmd_seen
)
4563 struct userData
*uData
;
4564 struct handle_info
*handle
;
4565 char seen
[INTERVALLEN
];
4569 if(!irccasecmp(argv
[1], chanserv
->nick
))
4571 reply("CSMSG_IS_CHANSERV");
4575 if(!(handle
= get_handle_info(argv
[1])))
4577 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4581 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4583 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4588 reply("CSMSG_USER_PRESENT", handle
->handle
);
4589 else if(uData
->seen
)
4590 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4592 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4594 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4595 reply("CSMSG_USER_VACATION", handle
->handle
);
4600 static MODCMD_FUNC(cmd_names
)
4602 struct userNode
*targ
;
4603 struct userData
*targData
;
4604 unsigned int ii
, pos
;
4607 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4609 targ
= channel
->members
.list
[ii
]->user
;
4610 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4613 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4616 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4620 if(IsUserSuspended(targData
))
4622 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4625 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4626 reply("CSMSG_END_NAMES", channel
->name
);
4631 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4633 switch(ntype
->visible_type
)
4635 case NOTE_VIS_ALL
: return 1;
4636 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4637 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4642 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4644 struct userData
*uData
;
4646 switch(ntype
->set_access_type
)
4648 case NOTE_SET_CHANNEL_ACCESS
:
4649 if(!user
->handle_info
)
4651 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4653 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4654 case NOTE_SET_CHANNEL_SETTER
:
4655 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4656 case NOTE_SET_PRIVILEGED
: default:
4657 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4661 static CHANSERV_FUNC(cmd_note
)
4663 struct chanData
*cData
;
4665 struct note_type
*ntype
;
4667 cData
= channel
->channel_info
;
4670 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4674 /* If no arguments, show all visible notes for the channel. */
4680 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4682 note
= iter_data(it
);
4683 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4686 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4687 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4690 reply("CSMSG_NOTELIST_END", channel
->name
);
4692 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4694 /* If one argument, show the named note. */
4697 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4698 && note_type_visible_to_user(cData
, note
->type
, user
))
4700 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4702 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4703 && note_type_visible_to_user(NULL
, ntype
, user
))
4705 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4710 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4714 /* Assume they're trying to set a note. */
4718 ntype
= dict_find(note_types
, argv
[1], NULL
);
4721 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4724 else if(note_type_settable_by_user(channel
, ntype
, user
))
4726 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4727 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4728 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4729 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4730 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4732 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4734 /* The note is viewable to staff only, so return 0
4735 to keep the invocation from getting logged (or
4736 regular users can see it in !events). */
4742 reply("CSMSG_NO_ACCESS");
4749 static CHANSERV_FUNC(cmd_delnote
)
4754 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4755 || !note_type_settable_by_user(channel
, note
->type
, user
))
4757 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4760 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4761 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4765 static CHANSERV_FUNC(cmd_events
)
4767 struct logSearch discrim
;
4768 struct logReport report
;
4769 unsigned int matches
, limit
;
4771 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4772 if(limit
< 1 || limit
> 200)
4775 memset(&discrim
, 0, sizeof(discrim
));
4776 discrim
.masks
.bot
= chanserv
;
4777 discrim
.masks
.channel_name
= channel
->name
;
4779 discrim
.masks
.command
= argv
[2];
4780 discrim
.limit
= limit
;
4781 discrim
.max_time
= INT_MAX
;
4782 discrim
.severities
= 1 << LOG_COMMAND
;
4783 report
.reporter
= chanserv
;
4785 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4787 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4789 reply("MSG_MATCH_COUNT", matches
);
4791 reply("MSG_NO_MATCHES");
4795 static CHANSERV_FUNC(cmd_say
)
4801 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4802 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4804 else if(GetUserH(argv
[1]))
4807 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4808 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4812 reply("MSG_NOT_TARGET_NAME");
4818 static CHANSERV_FUNC(cmd_emote
)
4824 /* CTCP is so annoying. */
4825 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4826 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4828 else if(GetUserH(argv
[1]))
4830 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4831 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4835 reply("MSG_NOT_TARGET_NAME");
4841 struct channelList
*
4842 chanserv_support_channels(void)
4844 return &chanserv_conf
.support_channels
;
4847 static CHANSERV_FUNC(cmd_expire
)
4849 int channel_count
= registered_channels
;
4850 expire_channels(NULL
);
4851 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4856 chanserv_expire_suspension(void *data
)
4858 struct suspended
*suspended
= data
;
4859 struct chanNode
*channel
;
4861 if(!suspended
->expires
|| (now
< suspended
->expires
))
4862 suspended
->revoked
= now
;
4863 channel
= suspended
->cData
->channel
;
4864 suspended
->cData
->channel
= channel
;
4865 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4866 if(!IsOffChannel(suspended
->cData
))
4868 struct mod_chanmode change
;
4869 mod_chanmode_init(&change
);
4871 change
.args
[0].mode
= MODE_CHANOP
;
4872 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4873 mod_chanmode_announce(chanserv
, channel
, &change
);
4877 static CHANSERV_FUNC(cmd_csuspend
)
4879 struct suspended
*suspended
;
4880 char reason
[MAXLEN
];
4881 time_t expiry
, duration
;
4882 struct userData
*uData
;
4886 if(IsProtected(channel
->channel_info
))
4888 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4892 if(argv
[1][0] == '!')
4894 else if(IsSuspended(channel
->channel_info
))
4896 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4897 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4901 if(!strcmp(argv
[1], "0"))
4903 else if((duration
= ParseInterval(argv
[1])))
4904 expiry
= now
+ duration
;
4907 reply("MSG_INVALID_DURATION", argv
[1]);
4911 unsplit_string(argv
+ 2, argc
- 2, reason
);
4913 suspended
= calloc(1, sizeof(*suspended
));
4914 suspended
->revoked
= 0;
4915 suspended
->issued
= now
;
4916 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4917 suspended
->expires
= expiry
;
4918 suspended
->reason
= strdup(reason
);
4919 suspended
->cData
= channel
->channel_info
;
4920 suspended
->previous
= suspended
->cData
->suspended
;
4921 suspended
->cData
->suspended
= suspended
;
4923 if(suspended
->expires
)
4924 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4926 if(IsSuspended(channel
->channel_info
))
4928 suspended
->previous
->revoked
= now
;
4929 if(suspended
->previous
->expires
)
4930 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4931 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4932 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4936 /* Mark all users in channel as absent. */
4937 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4946 /* Mark the channel as suspended, then part. */
4947 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4948 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4949 reply("CSMSG_SUSPENDED", channel
->name
);
4950 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4951 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4956 static CHANSERV_FUNC(cmd_cunsuspend
)
4958 struct suspended
*suspended
;
4959 char message
[MAXLEN
];
4961 if(!IsSuspended(channel
->channel_info
))
4963 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4967 suspended
= channel
->channel_info
->suspended
;
4969 /* Expire the suspension and join ChanServ to the channel. */
4970 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4971 chanserv_expire_suspension(suspended
);
4972 reply("CSMSG_UNSUSPENDED", channel
->name
);
4973 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4974 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4978 typedef struct chanservSearch
4986 unsigned long flags
;
4990 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4993 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4998 search
= malloc(sizeof(struct chanservSearch
));
4999 memset(search
, 0, sizeof(*search
));
5002 for(i
= 0; i
< argc
; i
++)
5004 /* Assume all criteria require arguments. */
5007 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
5011 if(!irccasecmp(argv
[i
], "name"))
5012 search
->name
= argv
[++i
];
5013 else if(!irccasecmp(argv
[i
], "registrar"))
5014 search
->registrar
= argv
[++i
];
5015 else if(!irccasecmp(argv
[i
], "unvisited"))
5016 search
->unvisited
= ParseInterval(argv
[++i
]);
5017 else if(!irccasecmp(argv
[i
], "registered"))
5018 search
->registered
= ParseInterval(argv
[++i
]);
5019 else if(!irccasecmp(argv
[i
], "flags"))
5022 if(!irccasecmp(argv
[i
], "nodelete"))
5023 search
->flags
|= CHANNEL_NODELETE
;
5024 else if(!irccasecmp(argv
[i
], "suspended"))
5025 search
->flags
|= CHANNEL_SUSPENDED
;
5028 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
5032 else if(!irccasecmp(argv
[i
], "limit"))
5033 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
5036 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
5041 if(search
->name
&& !strcmp(search
->name
, "*"))
5043 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
5044 search
->registrar
= 0;
5053 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5055 const char *name
= channel
->channel
->name
;
5056 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5057 (search
->registrar
&& !channel
->registrar
) ||
5058 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5059 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5060 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5061 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5068 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5070 struct chanData
*channel
;
5071 unsigned int matches
= 0;
5073 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5075 if(!chanserv_channel_match(channel
, search
))
5085 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5090 search_print(struct chanData
*channel
, void *data
)
5092 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5095 static CHANSERV_FUNC(cmd_search
)
5098 unsigned int matches
;
5099 channel_search_func action
;
5103 if(!irccasecmp(argv
[1], "count"))
5104 action
= search_count
;
5105 else if(!irccasecmp(argv
[1], "print"))
5106 action
= search_print
;
5109 reply("CSMSG_ACTION_INVALID", argv
[1]);
5113 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5117 if(action
== search_count
)
5118 search
->limit
= INT_MAX
;
5120 if(action
== search_print
)
5122 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5126 matches
= chanserv_channel_search(search
, action
, user
);
5129 reply("MSG_MATCH_COUNT", matches
);
5131 reply("MSG_NO_MATCHES");
5137 static CHANSERV_FUNC(cmd_unvisited
)
5139 struct chanData
*cData
;
5140 time_t interval
= chanserv_conf
.channel_expire_delay
;
5141 char buffer
[INTERVALLEN
];
5142 unsigned int limit
= 25, matches
= 0;
5146 interval
= ParseInterval(argv
[1]);
5148 limit
= atoi(argv
[2]);
5151 intervalString(buffer
, interval
, user
->handle_info
);
5152 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5154 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5156 if((now
- cData
->visited
) < interval
)
5159 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5160 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5167 static MODCMD_FUNC(chan_opt_defaulttopic
)
5173 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5175 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5179 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5181 free(channel
->channel_info
->topic
);
5182 if(topic
[0] == '*' && topic
[1] == 0)
5184 topic
= channel
->channel_info
->topic
= NULL
;
5188 topic
= channel
->channel_info
->topic
= strdup(topic
);
5189 if(channel
->channel_info
->topic_mask
5190 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5191 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5193 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5196 if(channel
->channel_info
->topic
)
5197 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5199 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5203 static MODCMD_FUNC(chan_opt_topicmask
)
5207 struct chanData
*cData
= channel
->channel_info
;
5210 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5212 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5216 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5218 if(cData
->topic_mask
)
5219 free(cData
->topic_mask
);
5220 if(mask
[0] == '*' && mask
[1] == 0)
5222 cData
->topic_mask
= 0;
5226 cData
->topic_mask
= strdup(mask
);
5228 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5229 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5230 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5234 if(channel
->channel_info
->topic_mask
)
5235 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5237 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5241 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5245 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5249 if(greeting
[0] == '*' && greeting
[1] == 0)
5253 unsigned int length
= strlen(greeting
);
5254 if(length
> chanserv_conf
.greeting_length
)
5256 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5259 *data
= strdup(greeting
);
5268 reply(name
, user_find_message(user
, "MSG_NONE"));
5272 static MODCMD_FUNC(chan_opt_greeting
)
5274 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5277 static MODCMD_FUNC(chan_opt_usergreeting
)
5279 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5282 static MODCMD_FUNC(chan_opt_modes
)
5284 struct mod_chanmode
*new_modes
;
5285 char modes
[MODELEN
];
5289 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5291 reply("CSMSG_NO_ACCESS");
5294 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5296 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5298 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5300 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5303 else if(new_modes
->argc
> 1)
5305 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5306 mod_chanmode_free(new_modes
);
5311 channel
->channel_info
->modes
= *new_modes
;
5312 modcmd_chanmode_announce(new_modes
);
5313 mod_chanmode_free(new_modes
);
5317 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5319 reply("CSMSG_SET_MODES", modes
);
5321 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5325 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5327 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5329 struct chanData
*cData
= channel
->channel_info
;
5334 /* Set flag according to value. */
5335 if(enabled_string(argv
[1]))
5337 cData
->flags
|= mask
;
5340 else if(disabled_string(argv
[1]))
5342 cData
->flags
&= ~mask
;
5347 reply("MSG_INVALID_BINARY", argv
[1]);
5353 /* Find current option value. */
5354 value
= (cData
->flags
& mask
) ? 1 : 0;
5358 reply(name
, user_find_message(user
, "MSG_ON"));
5360 reply(name
, user_find_message(user
, "MSG_OFF"));
5364 static MODCMD_FUNC(chan_opt_nodelete
)
5366 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5368 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5372 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5375 static MODCMD_FUNC(chan_opt_dynlimit
)
5377 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5380 static MODCMD_FUNC(chan_opt_offchannel
)
5382 struct chanData
*cData
= channel
->channel_info
;
5387 /* Set flag according to value. */
5388 if(enabled_string(argv
[1]))
5390 if(!IsOffChannel(cData
))
5391 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5392 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5395 else if(disabled_string(argv
[1]))
5397 if(IsOffChannel(cData
))
5399 struct mod_chanmode change
;
5400 mod_chanmode_init(&change
);
5402 change
.args
[0].mode
= MODE_CHANOP
;
5403 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5404 mod_chanmode_announce(chanserv
, channel
, &change
);
5406 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5411 reply("MSG_INVALID_BINARY", argv
[1]);
5417 /* Find current option value. */
5418 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5422 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5424 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5428 static MODCMD_FUNC(chan_opt_defaults
)
5430 struct userData
*uData
;
5431 struct chanData
*cData
;
5432 const char *confirm
;
5433 enum levelOption lvlOpt
;
5434 enum charOption chOpt
;
5436 cData
= channel
->channel_info
;
5437 uData
= GetChannelUser(cData
, user
->handle_info
);
5438 if(!uData
|| (uData
->access
< UL_OWNER
))
5440 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5443 confirm
= make_confirmation_string(uData
);
5444 if((argc
< 2) || strcmp(argv
[1], confirm
))
5446 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5449 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5450 cData
->modes
= chanserv_conf
.default_modes
;
5451 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5452 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5453 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5454 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5455 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5460 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5462 struct chanData
*cData
= channel
->channel_info
;
5463 struct userData
*uData
;
5464 unsigned short value
;
5468 if(!check_user_level(channel
, user
, option
, 1, 1))
5470 reply("CSMSG_CANNOT_SET");
5473 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5474 if(!value
&& strcmp(argv
[1], "0"))
5476 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5479 uData
= GetChannelUser(cData
, user
->handle_info
);
5480 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5482 reply("CSMSG_BAD_SETLEVEL");
5487 /* removing these level sets..
5489 if(value > cData->lvlOpts[lvlGiveOps])
5491 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5495 case lvlGiveHalfOps:
5496 if(value < cData->lvlOpts[lvlGiveVoice])
5498 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5503 if(value < cData->lvlOpts[lvlGiveVoice])
5505 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5511 /* This test only applies to owners, since non-owners
5512 * trying to set an option to above their level get caught
5513 * by the CSMSG_BAD_SETLEVEL test above.
5515 if(value
> uData
->access
)
5517 reply("CSMSG_BAD_SETTERS");
5524 cData
->lvlOpts
[option
] = value
;
5526 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5530 static MODCMD_FUNC(chan_opt_enfops
)
5532 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5535 static MODCMD_FUNC(chan_opt_enfhalfops
)
5537 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5540 static MODCMD_FUNC(chan_opt_giveops)
5542 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5545 static MODCMD_FUNC(chan_opt_givehalfops)
5547 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5550 static MODCMD_FUNC(chan_opt_enfmodes
)
5552 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5555 static MODCMD_FUNC(chan_opt_enftopic
)
5557 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5560 static MODCMD_FUNC(chan_opt_pubcmd
)
5562 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5565 static MODCMD_FUNC(chan_opt_setters
)
5567 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5570 static MODCMD_FUNC(chan_opt_ctcpusers
)
5572 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5575 static MODCMD_FUNC(chan_opt_userinfo
)
5577 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5581 static MODCMD_FUNC(chan_opt_givevoice)
5583 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5587 static MODCMD_FUNC(chan_opt_topicsnarf
)
5589 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5592 static MODCMD_FUNC(chan_opt_inviteme
)
5594 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5598 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5600 struct chanData
*cData
= channel
->channel_info
;
5601 int count
= charOptions
[option
].count
, index
;
5605 index
= atoi(argv
[1]);
5607 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5609 reply("CSMSG_INVALID_NUMERIC", index
);
5610 /* Show possible values. */
5611 for(index
= 0; index
< count
; index
++)
5612 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5616 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5620 /* Find current option value. */
5623 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5627 /* Somehow, the option value is corrupt; reset it to the default. */
5628 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5633 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5637 static MODCMD_FUNC(chan_opt_voice
)
5639 return channel_multiple_option(chVoice
, CSFUNC_ARGS
);
5642 static MODCMD_FUNC(chan_opt_protect
)
5644 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5647 static MODCMD_FUNC(chan_opt_toys
)
5649 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5652 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5654 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5657 static MODCMD_FUNC(chan_opt_topicrefresh
)
5659 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5662 static struct svccmd_list set_shows_list
;
5665 handle_svccmd_unbind(struct svccmd
*target
) {
5667 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5668 if(target
== set_shows_list
.list
[ii
])
5669 set_shows_list
.used
= 0;
5672 static CHANSERV_FUNC(cmd_set
)
5674 struct svccmd
*subcmd
;
5678 /* Check if we need to (re-)initialize set_shows_list. */
5679 if(!set_shows_list
.used
)
5681 if(!set_shows_list
.size
)
5683 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5684 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5686 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5688 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5689 sprintf(buf
, "%s %s", argv
[0], name
);
5690 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5693 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5696 svccmd_list_append(&set_shows_list
, subcmd
);
5702 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5704 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5706 subcmd
= set_shows_list
.list
[ii
];
5707 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5709 reply("CSMSG_CHANNEL_OPTIONS_END");
5713 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5714 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5717 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5720 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5722 reply("CSMSG_NO_ACCESS");
5726 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5730 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5732 struct userData
*uData
;
5734 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5737 reply("CSMSG_NOT_USER", channel
->name
);
5743 /* Just show current option value. */
5745 else if(enabled_string(argv
[1]))
5747 uData
->flags
|= mask
;
5749 else if(disabled_string(argv
[1]))
5751 uData
->flags
&= ~mask
;
5755 reply("MSG_INVALID_BINARY", argv
[1]);
5759 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5763 static MODCMD_FUNC(user_opt_autoop
)
5765 struct userData
*uData
;
5767 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5770 reply("CSMSG_NOT_USER", channel
->name
);
5773 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5774 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5776 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5777 /* TODO: add halfops error message? or is the op one generic enough? */
5780 static MODCMD_FUNC(user_opt_autoinvite
)
5782 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5785 static MODCMD_FUNC(user_opt_info
)
5787 struct userData
*uData
;
5790 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5794 /* If they got past the command restrictions (which require access)
5795 * but fail this test, we have some fool with security override on.
5797 reply("CSMSG_NOT_USER", channel
->name
);
5804 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5805 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5807 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5810 bp
= strcspn(infoline
, "\001");
5813 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5818 if(infoline
[0] == '*' && infoline
[1] == 0)
5821 uData
->info
= strdup(infoline
);
5824 reply("CSMSG_USET_INFO", uData
->info
);
5826 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5830 struct svccmd_list uset_shows_list
;
5832 static CHANSERV_FUNC(cmd_uset
)
5834 struct svccmd
*subcmd
;
5838 /* Check if we need to (re-)initialize uset_shows_list. */
5839 if(!uset_shows_list
.used
)
5843 "AutoOp", "AutoInvite", "Info"
5846 if(!uset_shows_list
.size
)
5848 uset_shows_list
.size
= ArrayLength(options
);
5849 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5851 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5853 const char *name
= options
[ii
];
5854 sprintf(buf
, "%s %s", argv
[0], name
);
5855 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5858 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5861 svccmd_list_append(&uset_shows_list
, subcmd
);
5867 /* Do this so options are presented in a consistent order. */
5868 reply("CSMSG_USER_OPTIONS");
5869 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5870 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5874 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5875 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5878 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5882 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5885 static CHANSERV_FUNC(cmd_giveownership
)
5887 struct handle_info
*new_owner_hi
;
5888 struct userData
*new_owner
, *curr_user
;
5889 struct chanData
*cData
= channel
->channel_info
;
5890 struct do_not_register
*dnr
;
5892 unsigned short co_access
;
5893 char reason
[MAXLEN
];
5896 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5897 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5898 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5900 struct userData
*owner
= NULL
;
5901 for(curr_user
= channel
->channel_info
->users
;
5903 curr_user
= curr_user
->next
)
5905 if(curr_user
->access
!= UL_OWNER
)
5909 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5916 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5918 char delay
[INTERVALLEN
];
5919 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5920 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5923 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5925 if(new_owner_hi
== user
->handle_info
)
5927 reply("CSMSG_NO_TRANSFER_SELF");
5930 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5935 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5939 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5943 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5945 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5948 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5949 if(!IsHelping(user
))
5950 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5952 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5955 if(new_owner
->access
>= UL_COOWNER
)
5956 co_access
= new_owner
->access
;
5958 co_access
= UL_COOWNER
;
5959 new_owner
->access
= UL_OWNER
;
5961 curr_user
->access
= co_access
;
5962 cData
->ownerTransfer
= now
;
5963 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5964 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5965 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5969 static CHANSERV_FUNC(cmd_suspend
)
5971 struct handle_info
*hi
;
5972 struct userData
*self
, *target
;
5975 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5976 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5977 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5979 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5982 if(target
->access
>= self
->access
)
5984 reply("MSG_USER_OUTRANKED", hi
->handle
);
5987 if(target
->flags
& USER_SUSPENDED
)
5989 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5994 target
->present
= 0;
5997 target
->flags
|= USER_SUSPENDED
;
5998 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
6002 static CHANSERV_FUNC(cmd_unsuspend
)
6004 struct handle_info
*hi
;
6005 struct userData
*self
, *target
;
6008 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
6009 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
6010 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6012 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6015 if(target
->access
>= self
->access
)
6017 reply("MSG_USER_OUTRANKED", hi
->handle
);
6020 if(!(target
->flags
& USER_SUSPENDED
))
6022 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
6025 target
->flags
&= ~USER_SUSPENDED
;
6026 scan_user_presence(target
, NULL
);
6027 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
6031 static MODCMD_FUNC(cmd_deleteme
)
6033 struct handle_info
*hi
;
6034 struct userData
*target
;
6035 const char *confirm_string
;
6036 unsigned short access
;
6039 hi
= user
->handle_info
;
6040 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
6042 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
6045 if(target
->access
== UL_OWNER
)
6047 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
6050 confirm_string
= make_confirmation_string(target
);
6051 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6053 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6056 access
= target
->access
;
6057 channel_name
= strdup(channel
->name
);
6058 del_channel_user(target
, 1);
6059 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6065 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6067 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6068 struct chanData
*cData
;
6071 for(cData
= channelList
; cData
; cData
= cData
->next
)
6073 if(IsSuspended(cData
))
6075 opt
= cData
->chOpts
[chTopicRefresh
];
6078 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6081 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6082 cData
->last_refresh
= refresh_num
;
6084 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6087 static CHANSERV_FUNC(cmd_unf
)
6091 char response
[MAXLEN
];
6092 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6093 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6094 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6097 reply("CSMSG_UNF_RESPONSE");
6101 static CHANSERV_FUNC(cmd_ping
)
6105 char response
[MAXLEN
];
6106 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6107 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6108 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6111 reply("CSMSG_PING_RESPONSE");
6115 static CHANSERV_FUNC(cmd_wut
)
6119 char response
[MAXLEN
];
6120 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6121 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6122 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6125 reply("CSMSG_WUT_RESPONSE");
6129 static CHANSERV_FUNC(cmd_8ball
)
6131 unsigned int i
, j
, accum
;
6136 for(i
=1; i
<argc
; i
++)
6137 for(j
=0; argv
[i
][j
]; j
++)
6138 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6139 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6142 char response
[MAXLEN
];
6143 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6144 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6147 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6151 static CHANSERV_FUNC(cmd_d
)
6153 unsigned long sides
, count
, modifier
, ii
, total
;
6154 char response
[MAXLEN
], *sep
;
6158 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6168 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6169 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6173 else if((sep
[0] == '-') && isdigit(sep
[1]))
6174 modifier
= strtoul(sep
, NULL
, 10);
6175 else if((sep
[0] == '+') && isdigit(sep
[1]))
6176 modifier
= strtoul(sep
+1, NULL
, 10);
6183 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6188 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6191 for(total
= ii
= 0; ii
< count
; ++ii
)
6192 total
+= (rand() % sides
) + 1;
6195 if((count
> 1) || modifier
)
6197 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6198 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6202 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6203 sprintf(response
, fmt
, total
, sides
);
6206 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6208 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6212 static CHANSERV_FUNC(cmd_huggle
)
6214 /* CTCP must be via PRIVMSG, never notice */
6216 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6218 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6222 static CHANSERV_FUNC(cmd_calc
)
6224 char response
[MAXLEN
];
6227 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6230 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6232 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6237 chanserv_adjust_limit(void *data
)
6239 struct mod_chanmode change
;
6240 struct chanData
*cData
= data
;
6241 struct chanNode
*channel
= cData
->channel
;
6244 if(IsSuspended(cData
))
6247 cData
->limitAdjusted
= now
;
6248 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6249 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6251 if(limit
> cData
->modes
.new_limit
)
6252 limit
= cData
->modes
.new_limit
;
6253 else if(limit
== cData
->modes
.new_limit
)
6257 mod_chanmode_init(&change
);
6258 change
.modes_set
= MODE_LIMIT
;
6259 change
.new_limit
= limit
;
6260 mod_chanmode_announce(chanserv
, channel
, &change
);
6264 handle_new_channel(struct chanNode
*channel
)
6266 struct chanData
*cData
;
6268 if(!(cData
= channel
->channel_info
))
6271 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6272 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6274 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6275 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6278 /* Welcome to my worst nightmare. Warning: Read (or modify)
6279 the code below at your own risk. */
6281 handle_join(struct modeNode
*mNode
)
6283 struct mod_chanmode change
;
6284 struct userNode
*user
= mNode
->user
;
6285 struct chanNode
*channel
= mNode
->channel
;
6286 struct chanData
*cData
;
6287 struct userData
*uData
= NULL
;
6288 struct banData
*bData
;
6289 struct handle_info
*handle
;
6290 unsigned int modes
= 0, info
= 0;
6293 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6296 cData
= channel
->channel_info
;
6297 if(channel
->members
.used
> cData
->max
)
6298 cData
->max
= channel
->members
.used
;
6300 /* Check for bans. If they're joining through a ban, one of two
6302 * 1: Join during a netburst, by riding the break. Kick them
6303 * unless they have ops or voice in the channel.
6304 * 2: They're allowed to join through the ban (an invite in
6305 * ircu2.10, or a +e on Hybrid, or something).
6306 * If they're not joining through a ban, and the banlist is not
6307 * full, see if they're on the banlist for the channel. If so,
6310 /* This is really, really stupid. not all banned people are kicked.
6311 * sometimes we like to leave them unkicked.
6312 * I tried to explain this to the srvx developers and
6313 * got insulted.. hence one reason for this fork.
6315 if(user->uplink->burst && !mNode->modes)
6318 for(ii = 0; ii < channel->banlist.used; ii++)
6320 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6322 ** Riding a netburst. Naughty. **
6323 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6330 mod_chanmode_init(&change
);
6332 if(channel
->banlist
.used
< MAXBANS
)
6334 /* Not joining through a ban. */
6335 for(bData
= cData
->bans
;
6336 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6337 bData
= bData
->next
);
6341 char kick_reason
[MAXLEN
];
6342 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6344 bData
->triggered
= now
;
6345 if(bData
!= cData
->bans
)
6347 /* Shuffle the ban to the head of the list. */
6349 bData
->next
->prev
= bData
->prev
;
6351 bData
->prev
->next
= bData
->next
;
6354 bData
->next
= cData
->bans
;
6357 cData
->bans
->prev
= bData
;
6358 cData
->bans
= bData
;
6361 change
.args
[0].mode
= MODE_BAN
;
6362 change
.args
[0].u
.hostmask
= bData
->mask
;
6363 mod_chanmode_announce(chanserv
, channel
, &change
);
6364 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6369 /* ChanServ will not modify the limits in join-flooded channels.
6370 It will also skip DynLimit processing when the user (or srvx)
6371 is bursting in, because there are likely more incoming. */
6372 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6373 && !user
->uplink
->burst
6374 && !channel
->join_flooded
6375 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6377 /* The user count has begun "bumping" into the channel limit,
6378 so set a timer to raise the limit a bit. Any previous
6379 timers are removed so three incoming users within the delay
6380 results in one limit change, not three. */
6382 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6383 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6386 if(channel
->join_flooded
)
6388 /* don't automatically give non users ops or voice during a join flood */
6390 /* EVERYONE is to get voice */
6391 else if(cData
->chOpts
[chVoice
] == 'a')
6392 modes
|= MODE_VOICE
;
6394 greeting
= cData
->greeting
;
6395 if(user
->handle_info
)
6397 handle
= user
->handle_info
;
6399 if(IsHelper(user
) && !IsHelping(user
))
6402 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6404 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6406 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6412 uData
= GetTrueChannelAccess(cData
, handle
);
6413 if(uData
&& !IsUserSuspended(uData
))
6415 /* non users getting voice are handled above. */
6416 if(IsUserAutoOp(uData
))
6418 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6419 modes
|= MODE_CHANOP
;
6420 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6421 modes
|= MODE_HALFOP
;
6422 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chVoice
] == 'p')
6423 modes
|= MODE_VOICE
;
6425 if(uData
->access
>= UL_PRESENT
)
6426 cData
->visited
= now
;
6427 if(cData
->user_greeting
)
6428 greeting
= cData
->user_greeting
;
6430 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6431 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6438 if(!user
->uplink
->burst
)
6442 if(modes
& MODE_CHANOP
) {
6443 modes
&= ~MODE_HALFOP
;
6444 modes
&= ~MODE_VOICE
;
6446 change
.args
[0].mode
= modes
;
6447 change
.args
[0].u
.member
= mNode
;
6448 mod_chanmode_announce(chanserv
, channel
, &change
);
6450 if(greeting
&& !user
->uplink
->burst
)
6451 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6453 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6459 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6461 struct mod_chanmode change
;
6462 struct userData
*channel
;
6463 unsigned int ii
, jj
;
6465 if(!user
->handle_info
)
6468 mod_chanmode_init(&change
);
6470 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6472 struct chanNode
*cn
;
6473 struct modeNode
*mn
;
6474 if(IsUserSuspended(channel
)
6475 || IsSuspended(channel
->channel
)
6476 || !(cn
= channel
->channel
->channel
))
6479 mn
= GetUserMode(cn
, user
);
6482 if(!IsUserSuspended(channel
)
6483 && IsUserAutoInvite(channel
)
6484 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6486 && !user
->uplink
->burst
)
6487 irc_invite(chanserv
, user
, cn
);
6491 if(channel
->access
>= UL_PRESENT
)
6492 channel
->channel
->visited
= now
;
6494 if(IsUserAutoOp(channel
))
6496 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6497 change
.args
[0].mode
= MODE_CHANOP
;
6498 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6499 change
.args
[0].mode
= MODE_HALFOP
;
6500 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6501 change
.args
[0].mode
= MODE_VOICE
;
6503 change
.args
[0].mode
= 0;
6504 change
.args
[0].u
.member
= mn
;
6505 if(change
.args
[0].mode
)
6506 mod_chanmode_announce(chanserv
, cn
, &change
);
6509 channel
->seen
= now
;
6510 channel
->present
= 1;
6513 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6515 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6516 struct banData
*ban
;
6518 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6519 || !channel
->channel_info
6520 || IsSuspended(channel
->channel_info
))
6522 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6523 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6525 if(jj
< channel
->banlist
.used
)
6527 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6529 char kick_reason
[MAXLEN
];
6530 if(!user_matches_glob(user
, ban
->mask
, 1))
6532 change
.args
[0].mode
= MODE_BAN
;
6533 change
.args
[0].u
.hostmask
= ban
->mask
;
6534 mod_chanmode_announce(chanserv
, channel
, &change
);
6535 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6536 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6537 ban
->triggered
= now
;
6542 if(IsSupportHelper(user
))
6544 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6546 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6548 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6556 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6558 struct chanData
*cData
;
6559 struct userData
*uData
;
6561 cData
= mn
->channel
->channel_info
;
6562 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6565 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6567 /* Allow for a bit of padding so that the limit doesn't
6568 track the user count exactly, which could get annoying. */
6569 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6571 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6572 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6576 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6578 scan_user_presence(uData
, mn
->user
);
6582 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6584 unsigned int ii
, jj
;
6585 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6587 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6588 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6590 if(jj
< mn
->user
->channels
.used
)
6593 if(ii
== chanserv_conf
.support_channels
.used
)
6594 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6599 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6601 struct userData
*uData
;
6603 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6604 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6605 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6608 if(protect_user(victim
, kicker
, channel
->channel_info
))
6610 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6611 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6614 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6619 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6621 struct chanData
*cData
;
6623 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6626 cData
= channel
->channel_info
;
6627 if(bad_topic(channel
, user
, channel
->topic
))
6628 { /* User doesnt have privs to set topics. Undo it */
6629 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6630 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6633 /* If there is a topic mask set, and the new topic doesnt match,
6634 * set the topic to mask + new_topic */
6635 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6637 char new_topic
[TOPICLEN
+1];
6638 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6641 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6642 /* and fall through to topicsnarf code below.. */
6644 else /* Topic couldnt fit into mask, was too long */
6646 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6647 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6648 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6652 /* With topicsnarf, grab the topic and save it as the default topic. */
6653 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6656 cData
->topic
= strdup(channel
->topic
);
6662 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6664 struct mod_chanmode
*bounce
= NULL
;
6665 unsigned int bnc
, ii
;
6668 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6671 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6672 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6674 char correct
[MAXLEN
];
6675 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6676 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6677 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6679 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6681 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6683 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6684 if(!protect_user(victim
, user
, channel
->channel_info
))
6687 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6690 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6691 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6692 if(bounce
->args
[bnc
].u
.member
)
6696 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6697 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6699 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6701 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6703 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6704 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6707 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6708 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6709 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6712 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6714 const char *ban
= change
->args
[ii
].u
.hostmask
;
6715 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6718 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6719 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6720 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6722 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6727 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6728 mod_chanmode_announce(chanserv
, channel
, bounce
);
6729 for(ii
= 0; ii
< change
->argc
; ++ii
)
6730 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6731 free((char*)bounce
->args
[ii
].u
.hostmask
);
6732 mod_chanmode_free(bounce
);
6737 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6739 struct chanNode
*channel
;
6740 struct banData
*bData
;
6741 struct mod_chanmode change
;
6742 unsigned int ii
, jj
;
6743 char kick_reason
[MAXLEN
];
6745 mod_chanmode_init(&change
);
6747 change
.args
[0].mode
= MODE_BAN
;
6748 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6750 channel
= user
->channels
.list
[ii
]->channel
;
6751 /* Need not check for bans if they're opped or voiced. */
6752 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6754 /* Need not check for bans unless channel registration is active. */
6755 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6757 /* Look for a matching ban already on the channel. */
6758 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6759 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6761 /* Need not act if we found one. */
6762 if(jj
< channel
->banlist
.used
)
6764 /* Look for a matching ban in this channel. */
6765 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6767 if(!user_matches_glob(user
, bData
->mask
, 1))
6769 change
.args
[0].u
.hostmask
= bData
->mask
;
6770 mod_chanmode_announce(chanserv
, channel
, &change
);
6771 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6772 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6773 bData
->triggered
= now
;
6774 break; /* we don't need to check any more bans in the channel */
6779 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6781 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6785 dict_remove2(handle_dnrs
, old_handle
, 1);
6786 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6787 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6792 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6794 struct userNode
*h_user
;
6796 if(handle
->channels
)
6798 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6799 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6801 while(handle
->channels
)
6802 del_channel_user(handle
->channels
, 1);
6807 handle_server_link(UNUSED_ARG(struct server
*server
))
6809 struct chanData
*cData
;
6811 for(cData
= channelList
; cData
; cData
= cData
->next
)
6813 if(!IsSuspended(cData
))
6814 cData
->may_opchan
= 1;
6815 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6816 && !cData
->channel
->join_flooded
6817 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6818 < chanserv_conf
.adjust_threshold
))
6820 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6821 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6827 chanserv_conf_read(void)
6831 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6832 struct mod_chanmode
*change
;
6833 struct string_list
*strlist
;
6834 struct chanNode
*chan
;
6837 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6839 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6842 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6843 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6844 chanserv_conf
.support_channels
.used
= 0;
6845 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6847 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6849 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6852 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6854 channelList_append(&chanserv_conf
.support_channels
, chan
);
6857 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6860 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6863 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6865 channelList_append(&chanserv_conf
.support_channels
, chan
);
6867 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6868 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6869 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6870 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6871 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6872 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6873 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6874 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6875 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6876 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6877 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6878 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6879 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6880 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6881 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6882 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6883 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6884 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6885 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6886 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6887 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6888 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6889 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6891 NickChange(chanserv
, str
, 0);
6892 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6893 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6894 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6895 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6896 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6897 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6898 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6899 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6900 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6901 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6902 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6903 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6904 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6905 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6906 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6907 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6908 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6911 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6912 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6913 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6915 chanserv_conf
.default_modes
= *change
;
6916 mod_chanmode_free(change
);
6918 free_string_list(chanserv_conf
.set_shows
);
6919 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6921 strlist
= string_list_copy(strlist
);
6924 static const char *list
[] = {
6925 /* free form text */
6926 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6927 /* options based on user level */
6928 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
6929 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6930 /* multiple choice options */
6931 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6932 /* binary options */
6933 "DynLimit", "NoDelete",
6938 strlist
= alloc_string_list(ArrayLength(list
)-1);
6939 for(ii
=0; list
[ii
]; ii
++)
6940 string_list_append(strlist
, strdup(list
[ii
]));
6942 chanserv_conf
.set_shows
= strlist
;
6943 /* We don't look things up now, in case the list refers to options
6944 * defined by modules initialized after this point. Just mark the
6945 * function list as invalid, so it will be initialized.
6947 set_shows_list
.used
= 0;
6948 free_string_list(chanserv_conf
.eightball
);
6949 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6952 strlist
= string_list_copy(strlist
);
6956 strlist
= alloc_string_list(4);
6957 string_list_append(strlist
, strdup("Yes."));
6958 string_list_append(strlist
, strdup("No."));
6959 string_list_append(strlist
, strdup("Maybe so."));
6961 chanserv_conf
.eightball
= strlist
;
6962 free_string_list(chanserv_conf
.old_ban_names
);
6963 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6965 strlist
= string_list_copy(strlist
);
6967 strlist
= alloc_string_list(2);
6968 chanserv_conf
.old_ban_names
= strlist
;
6969 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6970 off_channel
= str
? atoi(str
) : 0;
6974 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6977 struct note_type
*ntype
;
6980 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6982 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6985 if(!(ntype
= chanserv_create_note_type(key
)))
6987 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6991 /* Figure out set access */
6992 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6994 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6995 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6997 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6999 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
7000 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
7002 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
7004 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
7008 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
7009 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
7010 ntype
->set_access
.min_opserv
= 0;
7013 /* Figure out visibility */
7014 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
7015 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7016 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
7017 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7018 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
7019 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
7020 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
7021 ntype
->visible_type
= NOTE_VIS_ALL
;
7023 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
7025 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
7026 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
7030 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7032 struct handle_info
*handle
;
7033 struct userData
*uData
;
7034 char *seen
, *inf
, *flags
;
7036 unsigned short access
;
7038 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7040 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
7044 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
7045 if(access
> UL_OWNER
)
7047 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
7051 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7052 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7053 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7054 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7055 handle
= get_handle_info(key
);
7058 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7062 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7063 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7065 /* Upgrade: set autoop to the inverse of noautoop */
7066 if(chanserv_read_version
< 2)
7068 /* if noautoop is true, set autoop false, and vice versa */
7069 if(uData
->flags
& USER_NOAUTO_OP
)
7070 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7072 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7073 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
);
7079 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7081 struct banData
*bData
;
7082 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7083 time_t set_time
, triggered_time
, expires_time
;
7085 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7087 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7091 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7092 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7093 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7094 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7095 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7096 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7097 if (!reason
|| !owner
)
7100 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7101 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7103 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7105 expires_time
= set_time
+ atoi(s_duration
);
7109 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7112 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7115 static struct suspended
*
7116 chanserv_read_suspended(dict_t obj
)
7118 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7122 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7123 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7124 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7125 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7126 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7127 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7128 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7129 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7130 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7131 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7136 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7138 struct suspended
*suspended
;
7139 struct mod_chanmode
*modes
;
7140 struct chanNode
*cNode
;
7141 struct chanData
*cData
;
7142 struct dict
*channel
, *obj
;
7143 char *str
, *argv
[10];
7147 channel
= hir
->d
.object
;
7149 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7152 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7155 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7158 cData
= register_channel(cNode
, str
);
7161 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7165 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7167 enum levelOption lvlOpt
;
7168 enum charOption chOpt
;
7170 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7171 cData
->flags
= atoi(str
);
7173 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7175 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7177 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7178 else if(levelOptions
[lvlOpt
].old_flag
)
7180 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7181 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7183 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7187 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7189 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7191 cData
->chOpts
[chOpt
] = str
[0];
7194 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7196 enum levelOption lvlOpt
;
7197 enum charOption chOpt
;
7200 cData
->flags
= base64toint(str
, 5);
7201 count
= strlen(str
+= 5);
7202 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7205 if(levelOptions
[lvlOpt
].old_flag
)
7207 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7208 lvl
= levelOptions
[lvlOpt
].flag_value
;
7210 lvl
= levelOptions
[lvlOpt
].default_value
;
7212 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7214 case 'c': lvl
= UL_COOWNER
; break;
7215 case 'm': lvl
= UL_MANAGER
; break;
7216 case 'n': lvl
= UL_OWNER
+1; break;
7217 case 'o': lvl
= UL_OP
; break;
7218 case 'p': lvl
= UL_PEON
; break;
7219 case 'h': lvl
= UL_HALFOP
; break;
7220 case 'w': lvl
= UL_OWNER
; break;
7221 default: lvl
= 0; break;
7223 cData
->lvlOpts
[lvlOpt
] = lvl
;
7225 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7226 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7229 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7231 suspended
= chanserv_read_suspended(obj
);
7232 cData
->suspended
= suspended
;
7233 suspended
->cData
= cData
;
7234 /* We could use suspended->expires and suspended->revoked to
7235 * set the CHANNEL_SUSPENDED flag, but we don't. */
7237 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7239 suspended
= calloc(1, sizeof(*suspended
));
7240 suspended
->issued
= 0;
7241 suspended
->revoked
= 0;
7242 suspended
->suspender
= strdup(str
);
7243 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7244 suspended
->expires
= str
? atoi(str
) : 0;
7245 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7246 suspended
->reason
= strdup(str
? str
: "No reason");
7247 suspended
->previous
= NULL
;
7248 cData
->suspended
= suspended
;
7249 suspended
->cData
= cData
;
7253 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7254 suspended
= NULL
; /* to squelch a warning */
7257 if(IsSuspended(cData
)) {
7258 if(suspended
->expires
> now
)
7259 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7260 else if(suspended
->expires
)
7261 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7264 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7265 struct mod_chanmode change
;
7266 mod_chanmode_init(&change
);
7268 change
.args
[0].mode
= MODE_CHANOP
;
7269 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7270 mod_chanmode_announce(chanserv
, cNode
, &change
);
7273 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7274 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7275 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7276 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7277 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7278 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7279 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7280 cData
->max
= str
? atoi(str
) : 0;
7281 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7282 cData
->greeting
= str
? strdup(str
) : NULL
;
7283 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7284 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7285 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7286 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7287 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7288 cData
->topic
= str
? strdup(str
) : NULL
;
7290 if(!IsSuspended(cData
)
7291 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7292 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7293 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7294 cData
->modes
= *modes
;
7296 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7297 if(cData
->modes
.argc
> 1)
7298 cData
->modes
.argc
= 1;
7299 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7300 mod_chanmode_free(modes
);
7303 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7304 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7305 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7307 if(!cData
->users
&& !IsProtected(cData
))
7309 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7310 unregister_channel(cData
, "has empty user list.");
7314 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7315 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7316 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7318 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7319 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7321 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7322 struct record_data
*rd
= iter_data(it
);
7323 const char *note
, *setter
;
7325 if(rd
->type
!= RECDB_OBJECT
)
7327 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7331 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7333 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7335 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7339 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7340 if(!setter
) setter
= "<unknown>";
7341 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7349 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7351 const char *setter
, *reason
, *str
;
7352 struct do_not_register
*dnr
;
7354 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7357 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7360 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7363 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7366 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7369 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7371 dnr
->set
= atoi(str
);
7377 chanserv_version_read(struct dict
*section
)
7381 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7383 chanserv_read_version
= atoi(str
);
7384 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7388 chanserv_saxdb_read(struct dict
*database
)
7390 struct dict
*section
;
7393 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7394 chanserv_version_read(section
);
7396 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7397 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7398 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7400 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7401 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7402 chanserv_channel_read(iter_key(it
), iter_data(it
));
7404 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7405 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7406 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7412 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7414 int high_present
= 0;
7415 saxdb_start_record(ctx
, KEY_USERS
, 1);
7416 for(; uData
; uData
= uData
->next
)
7418 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7420 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7421 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7422 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7424 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7426 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7427 saxdb_end_record(ctx
);
7429 saxdb_end_record(ctx
);
7430 return high_present
;
7434 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7438 saxdb_start_record(ctx
, KEY_BANS
, 1);
7439 for(; bData
; bData
= bData
->next
)
7441 saxdb_start_record(ctx
, bData
->mask
, 0);
7442 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7443 if(bData
->triggered
)
7444 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7446 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7448 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7450 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7451 saxdb_end_record(ctx
);
7453 saxdb_end_record(ctx
);
7457 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7459 saxdb_start_record(ctx
, name
, 0);
7460 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7461 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7463 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7465 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7467 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7469 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7470 saxdb_end_record(ctx
);
7474 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7478 enum levelOption lvlOpt
;
7479 enum charOption chOpt
;
7481 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7483 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7484 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7486 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7487 if(channel
->registrar
)
7488 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7489 if(channel
->greeting
)
7490 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7491 if(channel
->user_greeting
)
7492 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7493 if(channel
->topic_mask
)
7494 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7495 if(channel
->suspended
)
7496 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7498 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7499 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7500 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7501 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7502 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7504 buf
[0] = channel
->chOpts
[chOpt
];
7506 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7508 saxdb_end_record(ctx
);
7510 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7512 mod_chanmode_format(&channel
->modes
, buf
);
7513 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7516 high_present
= chanserv_write_users(ctx
, channel
->users
);
7517 chanserv_write_bans(ctx
, channel
->bans
);
7519 if(dict_size(channel
->notes
))
7523 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7524 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7526 struct note
*note
= iter_data(it
);
7527 saxdb_start_record(ctx
, iter_key(it
), 0);
7528 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7529 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7530 saxdb_end_record(ctx
);
7532 saxdb_end_record(ctx
);
7535 if(channel
->ownerTransfer
)
7536 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7537 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7538 saxdb_end_record(ctx
);
7542 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7546 saxdb_start_record(ctx
, ntype
->name
, 0);
7547 switch(ntype
->set_access_type
)
7549 case NOTE_SET_CHANNEL_ACCESS
:
7550 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7552 case NOTE_SET_CHANNEL_SETTER
:
7553 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7555 case NOTE_SET_PRIVILEGED
: default:
7556 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7559 switch(ntype
->visible_type
)
7561 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7562 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7563 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7565 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7566 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7567 saxdb_end_record(ctx
);
7571 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7573 struct do_not_register
*dnr
;
7576 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7578 dnr
= iter_data(it
);
7579 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7581 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7582 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7583 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7584 saxdb_end_record(ctx
);
7589 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7592 struct chanData
*channel
;
7594 /* Version Control*/
7595 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
7596 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
7597 saxdb_end_record(ctx
);
7600 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7601 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7602 chanserv_write_note_type(ctx
, iter_data(it
));
7603 saxdb_end_record(ctx
);
7606 saxdb_start_record(ctx
, KEY_DNR
, 1);
7607 write_dnrs_helper(ctx
, handle_dnrs
);
7608 write_dnrs_helper(ctx
, plain_dnrs
);
7609 write_dnrs_helper(ctx
, mask_dnrs
);
7610 saxdb_end_record(ctx
);
7613 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7614 for(channel
= channelList
; channel
; channel
= channel
->next
)
7615 chanserv_write_channel(ctx
, channel
);
7616 saxdb_end_record(ctx
);
7622 chanserv_db_cleanup(void) {
7624 unreg_part_func(handle_part
);
7626 unregister_channel(channelList
, "terminating.");
7627 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7628 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7629 free(chanserv_conf
.support_channels
.list
);
7630 dict_delete(handle_dnrs
);
7631 dict_delete(plain_dnrs
);
7632 dict_delete(mask_dnrs
);
7633 dict_delete(note_types
);
7634 free_string_list(chanserv_conf
.eightball
);
7635 free_string_list(chanserv_conf
.old_ban_names
);
7636 free_string_list(chanserv_conf
.set_shows
);
7637 free(set_shows_list
.list
);
7638 free(uset_shows_list
.list
);
7641 struct userData
*helper
= helperList
;
7642 helperList
= helperList
->next
;
7647 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7648 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7649 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7652 init_chanserv(const char *nick
)
7654 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7655 conf_register_reload(chanserv_conf_read
);
7657 reg_server_link_func(handle_server_link
);
7659 reg_new_channel_func(handle_new_channel
);
7660 reg_join_func(handle_join
);
7661 reg_part_func(handle_part
);
7662 reg_kick_func(handle_kick
);
7663 reg_topic_func(handle_topic
);
7664 reg_mode_change_func(handle_mode
);
7665 reg_nick_change_func(handle_nick_change
);
7667 reg_auth_func(handle_auth
);
7668 reg_handle_rename_func(handle_rename
);
7669 reg_unreg_func(handle_unreg
);
7671 handle_dnrs
= dict_new();
7672 dict_set_free_data(handle_dnrs
, free
);
7673 plain_dnrs
= dict_new();
7674 dict_set_free_data(plain_dnrs
, free
);
7675 mask_dnrs
= dict_new();
7676 dict_set_free_data(mask_dnrs
, free
);
7678 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7679 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7680 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7681 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7682 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7683 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7684 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7685 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7686 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7687 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7689 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7691 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7692 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7694 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7695 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7696 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7697 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7698 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7700 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7701 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7702 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7703 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7704 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7705 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7707 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7708 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7709 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7710 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7712 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7713 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7714 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7715 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7716 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7717 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7718 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7719 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7720 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7721 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7723 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7724 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7725 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "hop", NULL
);
7726 DEFINE_COMMAND(unban
, 2, 0, "template", "hop", NULL
);
7727 DEFINE_COMMAND(unbanall
, 1, 0, "template", "hop", NULL
);
7728 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "hop", NULL
);
7729 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7730 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "hop", "flags", "+never_csuspend", NULL
);
7731 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7732 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7733 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7734 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7735 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7736 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7738 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "manager", NULL
);
7739 DEFINE_COMMAND(addlamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7740 DEFINE_COMMAND(addtimedlamer
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7742 /* if you change dellamer access, see also places
7743 * like unbanme which have manager hardcoded. */
7744 DEFINE_COMMAND(dellamer
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "manager", NULL
);
7745 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7747 DEFINE_COMMAND(lamers
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7749 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7751 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7752 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7753 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7754 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7755 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7756 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7757 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7758 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7759 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7760 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7761 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7762 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7764 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7765 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7767 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7768 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7769 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7770 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7772 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7773 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7774 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7775 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7776 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7778 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7779 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7780 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7781 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7782 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7783 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7784 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7786 /* Channel options */
7787 DEFINE_CHANNEL_OPTION(defaulttopic
);
7788 DEFINE_CHANNEL_OPTION(topicmask
);
7789 DEFINE_CHANNEL_OPTION(greeting
);
7790 DEFINE_CHANNEL_OPTION(usergreeting
);
7791 DEFINE_CHANNEL_OPTION(modes
);
7792 DEFINE_CHANNEL_OPTION(enfops
);
7793 DEFINE_CHANNEL_OPTION(enfhalfops
);
7794 /*DEFINE_CHANNEL_OPTION(giveops);
7795 DEFINE_CHANNEL_OPTION(givehalfops);
7797 DEFINE_CHANNEL_OPTION(voice
);
7798 DEFINE_CHANNEL_OPTION(protect
);
7799 DEFINE_CHANNEL_OPTION(enfmodes
);
7800 DEFINE_CHANNEL_OPTION(enftopic
);
7801 DEFINE_CHANNEL_OPTION(pubcmd
);
7802 /*DEFINE_CHANNEL_OPTION(givevoice);
7804 DEFINE_CHANNEL_OPTION(userinfo
);
7805 DEFINE_CHANNEL_OPTION(dynlimit
);
7806 DEFINE_CHANNEL_OPTION(topicsnarf
);
7807 DEFINE_CHANNEL_OPTION(nodelete
);
7808 DEFINE_CHANNEL_OPTION(toys
);
7809 DEFINE_CHANNEL_OPTION(setters
);
7810 DEFINE_CHANNEL_OPTION(topicrefresh
);
7811 DEFINE_CHANNEL_OPTION(ctcpusers
);
7812 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7813 DEFINE_CHANNEL_OPTION(inviteme
);
7815 DEFINE_CHANNEL_OPTION(offchannel
);
7816 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7818 /* Alias set topic to set defaulttopic for compatibility. */
7819 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7822 DEFINE_USER_OPTION(autoinvite
);
7823 DEFINE_USER_OPTION(info
);
7824 DEFINE_USER_OPTION(autoop
);
7826 /* Alias uset autovoice to uset autoop. */
7827 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
7829 note_types
= dict_new();
7830 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7833 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7834 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7835 service_register(chanserv
)->trigger
= '!';
7836 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7839 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7841 if(chanserv_conf
.channel_expire_frequency
)
7842 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7844 if(chanserv_conf
.refresh_period
)
7846 time_t next_refresh
;
7847 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7848 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7851 reg_exit_func(chanserv_db_cleanup
);
7852 message_register_table(msgtab
);