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"
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 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 enough channels (at least %d); use FORCE to override." },
136 /* Do-not-register channels */
137 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
138 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
139 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
140 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
141 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
142 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
143 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
144 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
145 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
146 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
147 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
149 /* Channel unregistration */
150 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
151 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
152 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
153 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
156 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
157 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
159 /* Channel merging */
160 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
161 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
162 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
163 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
164 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
166 /* Handle unregistration */
167 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
170 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
171 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
172 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
173 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
174 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
175 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
176 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
177 { "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." },
178 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
179 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
180 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
181 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
182 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
183 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
185 /* Removing yourself from a channel. */
186 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
187 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
188 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
190 /* User management */
191 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
192 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
193 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
194 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
195 { "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." },
196 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
197 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
198 { "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." },
199 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
200 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing */
201 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
202 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here, %s would like to add you to my userlist in channel %s, but you are not auth'd to $b$N$b. Please auth now, and you will be added. If you do not have an accont, type /msg $N help register" },
203 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
205 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
206 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
207 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
208 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
209 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
210 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
213 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
214 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
215 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
216 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
217 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
218 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
219 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
220 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
221 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
222 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
223 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
224 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
225 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
226 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
227 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
228 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
230 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
232 /* Channel management */
233 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
234 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
235 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
237 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
238 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
239 { "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" },
240 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
241 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
242 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
243 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
245 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
246 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
247 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
248 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
249 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
250 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
251 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
252 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
253 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
255 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveHalfOps (%d)." },
256 { "CSMSG_BAD_GIVEHOPS", "You cannot change GiveHalfOps to below GiveOps (%d)." },
257 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
259 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
260 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
261 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
262 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
263 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
264 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
265 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
266 { "CSMSG_SET_MODES", "$bModes $b %s" },
267 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
268 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
269 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
270 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
272 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
273 { "CSMSG_SET_GIVE_HALFOPS", "$bGiveHalfOps $b %d" },
275 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
276 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
277 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
278 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d" },
280 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
282 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
283 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
284 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
285 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
286 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
287 { "CSMSG_SET_VOICE", "$bvoice $b %d - %s" },
288 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
289 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
290 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
291 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
292 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
293 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
294 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
295 { "CSMSG_USET_INFO", "$bInfo $b %s" },
297 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
298 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
299 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
300 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
301 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
302 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
303 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
304 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
305 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
306 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
307 { "CSMSG_VOICE_NONE", "Noone will be auto-voiced" },
308 { "CSMSG_VOICE_PEON", "PEONs will be auto-voiced" },
309 { "CSMSG_VOICE_ALL", "Everyone will be auto-voiced" },
310 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
311 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
312 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
313 { "CSMSG_PROTECT_NONE", "No users will be protected." },
314 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
315 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
316 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
317 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
318 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
319 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
320 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
321 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
322 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
323 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
324 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
325 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
327 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
328 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
329 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
330 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
331 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
332 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
333 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
334 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
336 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
337 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
338 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
340 /* Channel userlist */
341 { "CSMSG_ACCESS_ALL_HEADER", "$b%s Users From Level %s To %s$b" },
342 { "CSMSG_ACCESS_SEARCH_HEADER", "$b%s Users From Level %s To %s Matching %s$b" },
343 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
344 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
345 { "CSMSG_BANS_HEADER", "$bBans in %s$b" },
347 /* Channel note list */
348 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
349 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
350 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
351 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
352 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
353 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
354 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
355 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
356 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
357 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
358 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
359 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
360 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
361 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
362 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
364 /* Channel [un]suspension */
365 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
366 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
367 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
368 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
369 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
370 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
371 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
373 /* Access information */
374 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
375 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
376 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
377 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
378 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
379 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
380 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
381 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
382 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
383 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
384 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
386 /* Seen information */
387 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
388 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
389 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
390 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
392 /* Names information */
393 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
394 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
396 /* Channel information */
397 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
398 { "CSMSG_BAR", "----------------------------------------"},
399 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
400 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
401 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
402 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
403 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
404 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
405 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
406 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
407 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
408 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
409 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
410 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
411 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
412 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
413 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
414 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
415 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
416 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
417 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
418 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
419 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
421 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
422 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
423 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
424 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
425 { "CSMSG_PEEK_OPS", "$bOps:$b" },
426 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
427 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
429 /* Network information */
430 { "CSMSG_NETWORK_INFO", "Network Information:" },
431 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
432 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
433 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
434 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
435 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
436 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
437 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
438 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
441 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
442 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
443 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
445 /* Channel searches */
446 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
447 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
448 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
449 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
451 /* Channel configuration */
452 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
453 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
454 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
455 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
458 { "CSMSG_USER_OPTIONS", "User Options:" },
459 { "CSMSG_USER_PROTECTED", "That user is protected." },
462 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
463 { "CSMSG_PING_RESPONSE", "Pong!" },
464 { "CSMSG_WUT_RESPONSE", "wut" },
465 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
466 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
467 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
468 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
469 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
470 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
471 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
474 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
478 /* eject_user and unban_user flags */
479 #define ACTION_KICK 0x0001
480 #define ACTION_BAN 0x0002
481 #define ACTION_ADD_BAN 0x0004
482 #define ACTION_ADD_TIMED_BAN 0x0008
483 #define ACTION_UNBAN 0x0010
484 #define ACTION_DEL_BAN 0x0020
486 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
487 #define MODELEN 40 + KEYLEN
491 #define CSFUNC_ARGS user, channel, argc, argv, cmd
493 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
494 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
495 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
496 reply("MSG_MISSING_PARAMS", argv[0]); \
500 DECLARE_LIST(dnrList
, struct do_not_register
*);
501 DEFINE_LIST(dnrList
, struct do_not_register
*);
503 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
505 struct userNode
*chanserv
;
508 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
509 static struct log_type
*CS_LOG
;
510 struct adduserPending
* adduser_pendings
= NULL
;
511 unsigned int adduser_pendings_count
= 0;
515 struct channelList support_channels
;
516 struct mod_chanmode default_modes
;
518 unsigned long db_backup_frequency
;
519 unsigned long channel_expire_frequency
;
522 unsigned int adjust_delay
;
523 long channel_expire_delay
;
524 unsigned int nodelete_level
;
526 unsigned int adjust_threshold
;
527 int join_flood_threshold
;
529 unsigned int greeting_length
;
530 unsigned int refresh_period
;
531 unsigned int giveownership_period
;
533 unsigned int max_owned
;
534 unsigned int max_chan_users
;
535 unsigned int max_chan_bans
;
536 unsigned int max_userinfo_length
;
538 struct string_list
*set_shows
;
539 struct string_list
*eightball
;
540 struct string_list
*old_ban_names
;
542 const char *ctcp_short_ban_duration
;
543 const char *ctcp_long_ban_duration
;
545 const char *irc_operator_epithet
;
546 const char *network_helper_epithet
;
547 const char *support_helper_epithet
;
552 struct userNode
*user
;
553 struct userNode
*bot
;
554 struct chanNode
*channel
;
556 unsigned short lowest
;
557 unsigned short highest
;
558 struct userData
**users
;
559 struct helpfile_table table
;
562 enum note_access_type
564 NOTE_SET_CHANNEL_ACCESS
,
565 NOTE_SET_CHANNEL_SETTER
,
569 enum note_visible_type
572 NOTE_VIS_CHANNEL_USERS
,
578 enum note_access_type set_access_type
;
580 unsigned int min_opserv
;
581 unsigned short min_ulevel
;
583 enum note_visible_type visible_type
;
584 unsigned int max_length
;
591 struct note_type
*type
;
592 char setter
[NICKSERV_HANDLE_LEN
+1];
596 static unsigned int registered_channels
;
597 static unsigned int banCount
;
599 static const struct {
602 unsigned short level
;
604 } accessLevels
[] = { /* MUST be orderd less to most! */
605 { "peon", "Peon", UL_PEON
, '+' },
606 { "halfop", "HalfOp", UL_HALFOP
, '%' },
607 { "op", "Op", UL_OP
, '@' },
608 { "manager", "Manager", UL_MANAGER
, '%' },
609 { "coowner", "Coowner", UL_COOWNER
, '*' },
610 { "owner", "Owner", UL_OWNER
, '!' },
611 { "helper", "BUG:", UL_HELPER
, 'X' }
614 static const struct {
617 unsigned short default_value
;
618 unsigned int old_idx
;
619 unsigned int old_flag
;
620 unsigned short flag_value
;
622 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
623 { "CSMSG_SET_GIVE_HALFOPS", "givehalfops", 150, ~0, CHANNEL_HOP_ALL
, 0 },
624 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
625 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
626 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
627 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
628 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
629 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
630 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
631 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
632 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
633 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
634 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
637 struct charOptionValues
{
641 { 'n', "CSMSG_VOICE_NONE" },
642 { 'p', "CSMSG_VOICE_PEON" },
643 { 'a', "CSMSG_VOICE_ALL" }
644 }, protectValues
[] = {
645 { 'a', "CSMSG_PROTECT_ALL" },
646 { 'e', "CSMSG_PROTECT_EQUAL" },
647 { 'l', "CSMSG_PROTECT_LOWER" },
648 { 'n', "CSMSG_PROTECT_NONE" }
650 { 'd', "CSMSG_TOYS_DISABLED" },
651 { 'n', "CSMSG_TOYS_PRIVATE" },
652 { 'p', "CSMSG_TOYS_PUBLIC" }
653 }, topicRefreshValues
[] = {
654 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
655 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
656 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
657 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
658 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
659 }, ctcpReactionValues
[] = {
660 { 'k', "CSMSG_CTCPREACTION_KICK" },
661 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
662 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
663 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
666 static const struct {
670 unsigned int old_idx
;
672 struct charOptionValues
*values
;
674 { "CSMSG_SET_VOICE", "voice", 'p', 99, ArrayLength(voiceValues
), voiceValues
},
675 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
676 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
677 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
678 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
681 struct userData
*helperList
;
682 struct chanData
*channelList
;
683 static struct module *chanserv_module
;
684 static unsigned int userCount
;
685 unsigned int chanserv_read_version
= 0; /* db version control */
687 #define CHANSERV_DB_VERSION 2
689 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
690 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
691 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
694 user_level_from_name(const char *name
, unsigned short clamp_level
)
696 unsigned int level
= 0, ii
;
698 level
= strtoul(name
, NULL
, 10);
699 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
700 if(!irccasecmp(name
, accessLevels
[ii
].name
))
701 level
= accessLevels
[ii
].level
;
702 if(level
> clamp_level
)
708 user_level_name_from_level(int level
)
716 for(ii
= 0; (ii
< ArrayLength(accessLevels
)); ii
++)
717 if(level
>= accessLevels
[ii
].level
)
718 highest
= accessLevels
[ii
].title
;
724 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
727 *minl
= strtoul(arg
, &sep
, 10);
735 *maxl
= strtoul(sep
+1, &sep
, 10);
743 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
745 struct userData
*uData
, **head
;
747 if(!channel
|| !handle
)
750 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
751 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
753 for(uData
= helperList
;
754 uData
&& uData
->handle
!= handle
;
755 uData
= uData
->next
);
759 uData
= calloc(1, sizeof(struct userData
));
760 uData
->handle
= handle
;
762 uData
->access
= UL_HELPER
;
768 uData
->next
= helperList
;
770 helperList
->prev
= uData
;
778 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
779 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
782 head
= &(channel
->users
);
785 if(uData
&& (uData
!= *head
))
787 /* Shuffle the user to the head of whatever list he was in. */
789 uData
->next
->prev
= uData
->prev
;
791 uData
->prev
->next
= uData
->next
;
797 (**head
).prev
= uData
;
804 /* Returns non-zero if user has at least the minimum access.
805 * exempt_owner is set when handling !set, so the owner can set things
808 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
810 struct userData
*uData
;
811 struct chanData
*cData
= channel
->channel_info
;
812 unsigned short minimum
= cData
->lvlOpts
[opt
];
815 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
818 if(minimum
<= uData
->access
)
820 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
825 /* Scan for other users authenticated to the same handle
826 still in the channel. If so, keep them listed as present.
828 user is optional, if not null, it skips checking that userNode
829 (for the handle_part function) */
831 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
835 if(IsSuspended(uData
->channel
)
836 || IsUserSuspended(uData
)
837 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
849 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
851 unsigned int eflags
, argc
;
853 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
855 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
856 if(!channel
->channel_info
857 || IsSuspended(channel
->channel_info
)
859 || !ircncasecmp(text
, "ACTION ", 7))
861 /* Figure out the minimum level needed to CTCP the channel */
862 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
864 /* We need to enforce against them; do so. */
867 argv
[1] = user
->nick
;
869 if(GetUserMode(channel
, user
))
870 eflags
|= ACTION_KICK
;
871 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
872 default: case 'k': /* just do the kick */ break;
874 eflags
|= ACTION_BAN
;
877 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
878 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
881 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
882 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
885 argv
[argc
++] = bad_ctcp_reason
;
886 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
890 chanserv_create_note_type(const char *name
)
892 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
893 strcpy(ntype
->name
, name
);
895 dict_insert(note_types
, ntype
->name
, ntype
);
900 chanserv_deref_note_type(void *data
)
902 struct note_type
*ntype
= data
;
904 if(--ntype
->refs
> 0)
910 chanserv_flush_note_type(struct note_type
*ntype
)
912 struct chanData
*cData
;
913 for(cData
= channelList
; cData
; cData
= cData
->next
)
914 dict_remove(cData
->notes
, ntype
->name
);
918 chanserv_truncate_notes(struct note_type
*ntype
)
920 struct chanData
*cData
;
922 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
924 for(cData
= channelList
; cData
; cData
= cData
->next
) {
925 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
928 if(strlen(note
->note
) <= ntype
->max_length
)
930 dict_remove2(cData
->notes
, ntype
->name
, 1);
931 note
= realloc(note
, size
);
932 note
->note
[ntype
->max_length
] = 0;
933 dict_insert(cData
->notes
, ntype
->name
, note
);
937 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
940 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
943 unsigned int len
= strlen(text
);
945 if(len
> type
->max_length
) len
= type
->max_length
;
946 note
= calloc(1, sizeof(*note
) + len
);
948 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
949 memcpy(note
->note
, text
, len
);
951 dict_insert(channel
->notes
, type
->name
, note
);
957 chanserv_free_note(void *data
)
959 struct note
*note
= data
;
961 chanserv_deref_note_type(note
->type
);
962 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
966 static MODCMD_FUNC(cmd_createnote
) {
967 struct note_type
*ntype
;
968 unsigned int arg
= 1, existed
= 0, max_length
;
970 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
973 ntype
= chanserv_create_note_type(argv
[arg
]);
974 if(!irccasecmp(argv
[++arg
], "privileged"))
977 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
978 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
980 else if(!irccasecmp(argv
[arg
], "channel"))
982 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
985 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
988 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
989 ntype
->set_access
.min_ulevel
= ulvl
;
991 else if(!irccasecmp(argv
[arg
], "setter"))
993 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
997 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1001 if(!irccasecmp(argv
[++arg
], "privileged"))
1002 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
1003 else if(!irccasecmp(argv
[arg
], "channel_users"))
1004 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
1005 else if(!irccasecmp(argv
[arg
], "all"))
1006 ntype
->visible_type
= NOTE_VIS_ALL
;
1008 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
1012 if((arg
+1) >= argc
) {
1013 reply("MSG_MISSING_PARAMS", argv
[0]);
1016 max_length
= strtoul(argv
[++arg
], NULL
, 0);
1017 if(max_length
< 20 || max_length
> 450)
1019 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
1022 if(existed
&& (max_length
< ntype
->max_length
))
1024 ntype
->max_length
= max_length
;
1025 chanserv_truncate_notes(ntype
);
1027 ntype
->max_length
= max_length
;
1030 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
1032 reply("CSMSG_NOTE_CREATED", ntype
->name
);
1037 dict_remove(note_types
, ntype
->name
);
1041 static MODCMD_FUNC(cmd_removenote
) {
1042 struct note_type
*ntype
;
1045 ntype
= dict_find(note_types
, argv
[1], NULL
);
1046 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
1049 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
1056 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
1059 chanserv_flush_note_type(ntype
);
1061 dict_remove(note_types
, argv
[1]);
1062 reply("CSMSG_NOTE_DELETED", argv
[1]);
1067 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1071 if(orig
->modes_set
& change
->modes_clear
)
1073 if(orig
->modes_clear
& change
->modes_set
)
1075 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1076 && strcmp(orig
->new_key
, change
->new_key
))
1078 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1079 && (orig
->new_limit
!= change
->new_limit
))
1084 static char max_length_text
[MAXLEN
+1][16];
1086 static struct helpfile_expansion
1087 chanserv_expand_variable(const char *variable
)
1089 struct helpfile_expansion exp
;
1091 if(!irccasecmp(variable
, "notes"))
1094 exp
.type
= HF_TABLE
;
1095 exp
.value
.table
.length
= 1;
1096 exp
.value
.table
.width
= 3;
1097 exp
.value
.table
.flags
= 0;
1098 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1099 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1100 exp
.value
.table
.contents
[0][0] = "Note Type";
1101 exp
.value
.table
.contents
[0][1] = "Visibility";
1102 exp
.value
.table
.contents
[0][2] = "Max Length";
1103 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1105 struct note_type
*ntype
= iter_data(it
);
1108 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1109 row
= exp
.value
.table
.length
++;
1110 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1111 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1112 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1113 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1115 if(!max_length_text
[ntype
->max_length
][0])
1116 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1117 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1122 exp
.type
= HF_STRING
;
1123 exp
.value
.str
= NULL
;
1127 static struct chanData
*
1128 register_channel(struct chanNode
*cNode
, char *registrar
)
1130 struct chanData
*channel
;
1131 enum levelOption lvlOpt
;
1132 enum charOption chOpt
;
1134 channel
= calloc(1, sizeof(struct chanData
));
1136 channel
->notes
= dict_new();
1137 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1139 channel
->registrar
= strdup(registrar
);
1140 channel
->registered
= now
;
1141 channel
->visited
= now
;
1142 channel
->limitAdjusted
= now
;
1143 channel
->ownerTransfer
= now
;
1144 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1145 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1146 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1147 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1148 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1150 channel
->prev
= NULL
;
1151 channel
->next
= channelList
;
1154 channelList
->prev
= channel
;
1155 channelList
= channel
;
1156 registered_channels
++;
1158 channel
->channel
= cNode
;
1160 cNode
->channel_info
= channel
;
1165 static struct userData
*
1166 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1168 struct userData
*ud
;
1170 if(access
> UL_OWNER
)
1173 ud
= calloc(1, sizeof(*ud
));
1174 ud
->channel
= channel
;
1175 ud
->handle
= handle
;
1177 ud
->access
= access
;
1178 ud
->info
= info
? strdup(info
) : NULL
;
1181 ud
->next
= channel
->users
;
1183 channel
->users
->prev
= ud
;
1184 channel
->users
= ud
;
1186 channel
->userCount
++;
1190 ud
->u_next
= ud
->handle
->channels
;
1192 ud
->u_next
->u_prev
= ud
;
1193 ud
->handle
->channels
= ud
;
1195 ud
->flags
= USER_FLAGS_DEFAULT
;
1199 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1202 del_channel_user(struct userData
*user
, int do_gc
)
1204 struct chanData
*channel
= user
->channel
;
1206 channel
->userCount
--;
1210 user
->prev
->next
= user
->next
;
1212 channel
->users
= user
->next
;
1214 user
->next
->prev
= user
->prev
;
1217 user
->u_prev
->u_next
= user
->u_next
;
1219 user
->handle
->channels
= user
->u_next
;
1221 user
->u_next
->u_prev
= user
->u_prev
;
1225 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1226 unregister_channel(channel
, "lost all users.");
1229 static struct adduserPending
*
1230 add_adduser_pending(struct chanNode
*channel
, struct userNode
*user
, int level
)
1232 struct adduserPending
*ap
;
1233 ap
= calloc(1,sizeof(struct adduserPending
));
1234 ap
->channel
= channel
;
1237 ap
->created
= time(NULL
);
1239 /* ap->prev defaults to NULL already.. */
1240 ap
->next
= adduser_pendings
;
1241 if(adduser_pendings
)
1242 adduser_pendings
->prev
= ap
;
1243 adduser_pendings
= ap
;
1244 adduser_pendings_count
++;
1249 del_adduser_pending(struct adduserPending
*ap
)
1252 ap
->prev
->next
= ap
->next
;
1254 adduser_pendings
= ap
->next
;
1257 ap
->next
->prev
= ap
->prev
;
1261 static void expire_adduser_pending();
1263 /* find_adduser_pending(channel, user) will find an arbitrary record
1264 * from user, channel, or user and channel.
1265 * if user or channel are NULL, they will match any records.
1267 static struct adduserPending
*
1268 find_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1270 struct adduserPending
*ap
;
1272 expire_adduser_pending(); /* why not here.. */
1274 if(!channel
&& !user
) /* 2 nulls matches all */
1275 return(adduser_pendings
);
1276 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
1278 if((channel
== ap
->channel
&& (user
== NULL
|| user
== ap
->user
)) || (user
==ap
->user
&& channel
==NULL
))
1285 /* Remove all pendings for a user or channel
1287 * called in nickserv.c DelUser() and proto-* unregister_channel()
1290 wipe_adduser_pending(struct chanNode
*channel
, struct userNode
*user
)
1292 struct adduserPending
*ap
;
1294 /* So this is a bit wastefull, i hate dealing with linked lists.
1295 * if its a problem we'll rewrite it right */
1296 while((ap
= find_adduser_pending(channel
, user
))) {
1297 del_adduser_pending(ap
);
1301 /* Called from nickserv.c cmd_auth after someone auths */
1303 process_adduser_pending(struct userNode
*user
)
1305 struct adduserPending
*ap
;
1306 while((ap
= find_adduser_pending(NULL
, user
)))
1308 struct userData
*actee
;
1309 actee
= add_channel_user(ap
->channel
->channel_info
, ap
->user
->handle_info
, ap
->level
, 0, NULL
);
1310 scan_user_presence(actee
, NULL
);
1311 del_adduser_pending(ap
);
1316 expire_adduser_pending()
1318 struct adduserPending
*ap
, *ap_next
;
1319 ap
= adduser_pendings
;
1322 if((ap
->created
+ ADDUSER_PENDING_EXPIRE
) < time(NULL
))
1324 ap_next
= ap
->next
; /* save next */
1325 del_adduser_pending(ap
); /* free and relink */
1326 ap
= ap_next
; /* advance */
1333 static void expire_ban(void *data
);
1335 static struct banData
*
1336 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1339 unsigned int ii
, l1
, l2
;
1344 bd
= malloc(sizeof(struct banData
));
1346 bd
->channel
= channel
;
1348 bd
->triggered
= triggered
;
1349 bd
->expires
= expires
;
1351 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1353 extern const char *hidden_host_suffix
;
1354 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1358 l2
= strlen(old_name
);
1361 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1363 new_mask
= alloca(MAXLEN
);
1364 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), mask
, hidden_host_suffix
);
1367 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1369 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1370 bd
->reason
= strdup(reason
);
1373 timeq_add(expires
, expire_ban
, bd
);
1376 bd
->next
= channel
->bans
;
1378 channel
->bans
->prev
= bd
;
1380 channel
->banCount
++;
1387 del_channel_ban(struct banData
*ban
)
1389 ban
->channel
->banCount
--;
1393 ban
->prev
->next
= ban
->next
;
1395 ban
->channel
->bans
= ban
->next
;
1398 ban
->next
->prev
= ban
->prev
;
1401 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1410 expire_ban(void *data
)
1412 struct banData
*bd
= data
;
1413 if(!IsSuspended(bd
->channel
))
1415 struct banList bans
;
1416 struct mod_chanmode change
;
1418 bans
= bd
->channel
->channel
->banlist
;
1419 mod_chanmode_init(&change
);
1420 for(ii
=0; ii
<bans
.used
; ii
++)
1422 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1425 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1426 change
.args
[0].u
.hostmask
= bd
->mask
;
1427 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1433 del_channel_ban(bd
);
1436 static void chanserv_expire_suspension(void *data
);
1439 unregister_channel(struct chanData
*channel
, const char *reason
)
1441 struct mod_chanmode change
;
1442 char msgbuf
[MAXLEN
];
1444 /* After channel unregistration, the following must be cleaned
1446 - Channel information.
1449 - Channel suspension data.
1450 - adduser_pending data.
1451 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1457 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1461 mod_chanmode_init(&change
);
1462 change
.modes_clear
|= MODE_REGISTERED
;
1463 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1466 wipe_adduser_pending(channel
->channel
, NULL
);
1468 while(channel
->users
)
1469 del_channel_user(channel
->users
, 0);
1471 while(channel
->bans
)
1472 del_channel_ban(channel
->bans
);
1474 free(channel
->topic
);
1475 free(channel
->registrar
);
1476 free(channel
->greeting
);
1477 free(channel
->user_greeting
);
1478 free(channel
->topic_mask
);
1481 channel
->prev
->next
= channel
->next
;
1483 channelList
= channel
->next
;
1486 channel
->next
->prev
= channel
->prev
;
1488 if(channel
->suspended
)
1490 struct chanNode
*cNode
= channel
->channel
;
1491 struct suspended
*suspended
, *next_suspended
;
1493 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1495 next_suspended
= suspended
->previous
;
1496 free(suspended
->suspender
);
1497 free(suspended
->reason
);
1498 if(suspended
->expires
)
1499 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1504 cNode
->channel_info
= NULL
;
1506 channel
->channel
->channel_info
= NULL
;
1508 dict_delete(channel
->notes
);
1509 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1510 if(!IsSuspended(channel
))
1511 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1512 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1513 UnlockChannel(channel
->channel
);
1515 registered_channels
--;
1519 expire_channels(UNUSED_ARG(void *data
))
1521 struct chanData
*channel
, *next
;
1522 struct userData
*user
;
1523 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1525 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1526 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1528 for(channel
= channelList
; channel
; channel
= next
)
1530 next
= channel
->next
;
1532 /* See if the channel can be expired. */
1533 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1534 || IsProtected(channel
))
1537 /* Make sure there are no high-ranking users still in the channel. */
1538 for(user
=channel
->users
; user
; user
=user
->next
)
1539 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1544 /* Unregister the channel */
1545 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1546 unregister_channel(channel
, "registration expired.");
1549 if(chanserv_conf
.channel_expire_frequency
)
1550 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1554 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1556 char protect
= channel
->chOpts
[chProtect
];
1557 struct userData
*cs_victim
, *cs_aggressor
;
1559 /* Don't protect if no one is to be protected, someone is attacking
1560 himself, or if the aggressor is an IRC Operator. */
1561 if(protect
== 'n' || victim
== aggressor
/* Opers dont get special treatment :/ || IsOper(aggressor) */)
1564 /* Don't protect if the victim isn't authenticated (because they
1565 can't be a channel user), unless we are to protect non-users
1567 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1568 if(protect
!= 'a' && !cs_victim
)
1571 /* Protect if the aggressor isn't a user because at this point,
1572 the aggressor can only be less than or equal to the victim. */
1573 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1577 /* If the aggressor was a user, then the victim can't be helped. */
1584 if(cs_victim
->access
> cs_aggressor
->access
)
1589 if(cs_victim
->access
>= cs_aggressor
->access
)
1598 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1600 struct chanData
*cData
= channel
->channel_info
;
1601 struct userData
*cs_victim
;
1603 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1604 || (cs_victim
->access
< UL_OP
/* cData->lvlOpts[lvlGiveOps]*/))
1605 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1607 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1615 validate_halfop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1617 struct chanData
*cData
= channel
->channel_info
;
1618 struct userData
*cs_victim
;
1620 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1621 || (cs_victim
->access
< UL_HALFOP
/* cData->lvlOpts[lvlGiveHalfOps] */))
1622 && !check_user_level(channel
, user
, lvlEnfHalfOps
, 0, 0))
1624 send_message(user
, chanserv
, "CSMSG_HOPBY_LOCKED");
1633 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1635 if(IsService(victim
))
1637 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1641 if(protect_user(victim
, user
, channel
->channel_info
))
1643 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1651 validate_dehop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1653 if(IsService(victim
))
1655 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1659 if(protect_user(victim
, user
, channel
->channel_info
))
1661 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1668 static struct do_not_register
*
1669 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1671 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1672 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1673 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1674 strcpy(dnr
->reason
, reason
);
1676 if(dnr
->chan_name
[0] == '*')
1677 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1678 else if(strpbrk(dnr
->chan_name
, "*?"))
1679 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1681 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1685 static struct dnrList
1686 chanserv_find_dnrs(const char *chan_name
, const char *handle
)
1688 struct dnrList list
;
1690 struct do_not_register
*dnr
;
1692 dnrList_init(&list
);
1693 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
, NULL
)))
1694 dnrList_append(&list
, dnr
);
1695 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1696 dnrList_append(&list
, dnr
);
1698 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1699 if(match_ircglob(chan_name
, iter_key(it
)))
1700 dnrList_append(&list
, iter_data(it
));
1705 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, const char *handle
)
1707 struct dnrList list
;
1708 struct do_not_register
*dnr
;
1710 char buf
[INTERVALLEN
];
1712 list
= chanserv_find_dnrs(chan_name
, handle
);
1713 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1715 dnr
= list
.list
[ii
];
1718 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1719 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1722 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1725 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1730 struct do_not_register
*
1731 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1733 struct do_not_register
*dnr
;
1736 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1740 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1742 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1743 if(match_ircglob(chan_name
, iter_key(it
)))
1744 return iter_data(it
);
1749 static CHANSERV_FUNC(cmd_noregister
)
1752 struct do_not_register
*dnr
;
1753 char buf
[INTERVALLEN
];
1754 unsigned int matches
;
1760 reply("CSMSG_DNR_SEARCH_RESULTS");
1763 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1765 dnr
= iter_data(it
);
1767 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1769 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1772 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1774 dnr
= iter_data(it
);
1776 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1778 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1781 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1783 dnr
= iter_data(it
);
1785 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1787 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1792 reply("MSG_MATCH_COUNT", matches
);
1794 reply("MSG_NO_MATCHES");
1800 if(!IsChannelName(target
) && (*target
!= '*'))
1802 reply("CSMSG_NOT_DNR", target
);
1808 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1809 if((*target
== '*') && !get_handle_info(target
+ 1))
1811 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1814 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1815 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1819 reply("CSMSG_DNR_SEARCH_RESULTS");
1822 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, target
+ 1);
1824 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1826 reply("MSG_NO_MATCHES");
1830 static CHANSERV_FUNC(cmd_allowregister
)
1832 const char *chan_name
= argv
[1];
1834 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1836 dict_remove(handle_dnrs
, chan_name
+1);
1837 reply("CSMSG_DNR_REMOVED", chan_name
);
1839 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1841 dict_remove(plain_dnrs
, chan_name
);
1842 reply("CSMSG_DNR_REMOVED", chan_name
);
1844 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1846 dict_remove(mask_dnrs
, chan_name
);
1847 reply("CSMSG_DNR_REMOVED", chan_name
);
1851 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1858 chanserv_get_owned_count(struct handle_info
*hi
)
1860 struct userData
*cList
;
1863 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1864 if(cList
->access
== UL_OWNER
)
1869 static CHANSERV_FUNC(cmd_register
)
1871 struct handle_info
*handle
;
1872 struct chanData
*cData
;
1873 struct modeNode
*mn
;
1874 char reason
[MAXLEN
];
1876 unsigned int new_channel
, force
=0;
1877 struct do_not_register
*dnr
;
1881 if(channel
->channel_info
)
1883 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1887 if(channel
->bad_channel
)
1889 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1894 && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1896 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1901 chan_name
= channel
->name
;
1907 reply("MSG_MISSING_PARAMS", cmd
->name
);
1908 svccmd_send_help_brief(user
, chanserv
, cmd
);
1911 if(!IsChannelName(argv
[1]))
1913 reply("MSG_NOT_CHANNEL_NAME");
1917 if(opserv_bad_channel(argv
[1]))
1919 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1924 chan_name
= argv
[1];
1927 if(argc
>= (new_channel
+2))
1929 if(!IsHelping(user
))
1931 reply("CSMSG_PROXY_FORBIDDEN");
1935 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1937 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1938 dnr
= chanserv_is_dnr(chan_name
, handle
);
1942 handle
= user
->handle_info
;
1943 dnr
= chanserv_is_dnr(chan_name
, handle
);
1947 if(!IsHelping(user
))
1948 reply("CSMSG_DNR_CHANNEL", chan_name
);
1950 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
->handle
);
1954 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1956 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1961 channel
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
1963 cData
= register_channel(channel
, user
->handle_info
->handle
);
1964 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1965 cData
->modes
= chanserv_conf
.default_modes
;
1967 cData
->modes
.modes_set
|= MODE_REGISTERED
;
1968 if (IsOffChannel(cData
))
1970 mod_chanmode_announce(chanserv
, channel
, &cData
->modes
);
1974 struct mod_chanmode
*change
= mod_chanmode_dup(&cData
->modes
, 1);
1975 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1976 change
->args
[change
->argc
].u
.member
= AddChannelUser(chanserv
, channel
);
1978 mod_chanmode_announce(chanserv
, channel
, change
);
1979 mod_chanmode_free(change
);
1982 /* Initialize the channel's max user record. */
1983 cData
->max
= channel
->members
.used
;
1985 if(handle
!= user
->handle_info
)
1986 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1988 reply("CSMSG_REG_SUCCESS", channel
->name
);
1990 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1991 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1996 make_confirmation_string(struct userData
*uData
)
1998 static char strbuf
[16];
2003 for(src
= uData
->handle
->handle
; *src
; )
2004 accum
= accum
* 31 + toupper(*src
++);
2006 for(src
= uData
->channel
->channel
->name
; *src
; )
2007 accum
= accum
* 31 + toupper(*src
++);
2008 sprintf(strbuf
, "%08x", accum
);
2012 static CHANSERV_FUNC(cmd_unregister
)
2015 char reason
[MAXLEN
];
2016 struct chanData
*cData
;
2017 struct userData
*uData
;
2019 cData
= channel
->channel_info
;
2022 reply("CSMSG_NOT_REGISTERED", channel
->name
);
2026 uData
= GetChannelUser(cData
, user
->handle_info
);
2027 if(!uData
|| (uData
->access
< UL_OWNER
))
2029 reply("CSMSG_NO_ACCESS");
2033 if(IsProtected(cData
))
2035 reply("CSMSG_UNREG_NODELETE", channel
->name
);
2039 if(!IsHelping(user
))
2041 const char *confirm_string
;
2042 if(IsSuspended(cData
))
2044 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
2047 confirm_string
= make_confirmation_string(uData
);
2048 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
2050 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
2055 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
2056 name
= strdup(channel
->name
);
2057 unregister_channel(cData
, reason
);
2058 reply("CSMSG_UNREG_SUCCESS", name
);
2063 static CHANSERV_FUNC(cmd_move
)
2065 struct mod_chanmode change
;
2066 struct chanNode
*target
;
2067 struct modeNode
*mn
;
2068 struct userData
*uData
;
2069 char reason
[MAXLEN
];
2070 struct do_not_register
*dnr
;
2074 if(IsProtected(channel
->channel_info
))
2076 reply("CSMSG_MOVE_NODELETE", channel
->name
);
2080 if(!IsChannelName(argv
[1]))
2082 reply("MSG_NOT_CHANNEL_NAME");
2086 if(opserv_bad_channel(argv
[1]))
2088 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
2092 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
2094 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
2096 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
2098 if(!IsHelping(user
))
2099 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
2101 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
->handle
);
2107 mod_chanmode_init(&change
);
2108 if(!(target
= GetChannel(argv
[1])))
2110 target
= AddChannel(argv
[1], now
, NULL
, NULL
, NULL
);
2111 if(!IsSuspended(channel
->channel_info
))
2112 AddChannelUser(chanserv
, target
);
2114 else if(target
->channel_info
)
2116 reply("CSMSG_ALREADY_REGGED", target
->name
);
2119 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
2120 && !IsHelping(user
))
2122 reply("CSMSG_MUST_BE_OPPED", target
->name
);
2125 else if(!IsSuspended(channel
->channel_info
))
2128 change
.args
[0].mode
= MODE_CHANOP
;
2129 change
.args
[0].u
.member
= AddChannelUser(chanserv
, target
);
2130 mod_chanmode_announce(chanserv
, target
, &change
);
2135 /* Clear MODE_REGISTERED from old channel, add it to new. */
2137 change
.modes_clear
= MODE_REGISTERED
;
2138 mod_chanmode_announce(chanserv
, channel
, &change
);
2139 change
.modes_clear
= 0;
2140 change
.modes_set
= MODE_REGISTERED
;
2141 mod_chanmode_announce(chanserv
, target
, &change
);
2144 /* Move the channel_info to the target channel; it
2145 shouldn't be necessary to clear timeq callbacks
2146 for the old channel. */
2147 target
->channel_info
= channel
->channel_info
;
2148 target
->channel_info
->channel
= target
;
2149 channel
->channel_info
= NULL
;
2151 reply("CSMSG_MOVE_SUCCESS", target
->name
);
2153 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
2154 if(!IsSuspended(target
->channel_info
))
2156 char reason2
[MAXLEN
];
2157 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
2158 DelChannelUser(chanserv
, channel
, reason2
, 0);
2160 UnlockChannel(channel
);
2161 LockChannel(target
);
2162 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
2167 merge_users(struct chanData
*source
, struct chanData
*target
)
2169 struct userData
*suData
, *tuData
, *next
;
2175 /* Insert the source's users into the scratch area. */
2176 for(suData
= source
->users
; suData
; suData
= suData
->next
)
2177 dict_insert(merge
, suData
->handle
->handle
, suData
);
2179 /* Iterate through the target's users, looking for
2180 users common to both channels. The lower access is
2181 removed from either the scratch area or target user
2183 for(tuData
= target
->users
; tuData
; tuData
= next
)
2185 struct userData
*choice
;
2187 next
= tuData
->next
;
2189 /* If a source user exists with the same handle as a target
2190 channel's user, resolve the conflict by removing one. */
2191 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
2195 /* Pick the data we want to keep. */
2196 /* If the access is the same, use the later seen time. */
2197 if(suData
->access
== tuData
->access
)
2198 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
2199 else /* Otherwise, keep the higher access level. */
2200 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
2202 /* Remove the user that wasn't picked. */
2203 if(choice
== tuData
)
2205 dict_remove(merge
, suData
->handle
->handle
);
2206 del_channel_user(suData
, 0);
2209 del_channel_user(tuData
, 0);
2212 /* Move the remaining users to the target channel. */
2213 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
2215 suData
= iter_data(it
);
2217 /* Insert the user into the target channel's linked list. */
2218 suData
->prev
= NULL
;
2219 suData
->next
= target
->users
;
2220 suData
->channel
= target
;
2223 target
->users
->prev
= suData
;
2224 target
->users
= suData
;
2226 /* Update the user counts for the target channel; the
2227 source counts are left alone. */
2228 target
->userCount
++;
2231 /* Possible to assert (source->users == NULL) here. */
2232 source
->users
= NULL
;
2237 merge_bans(struct chanData
*source
, struct chanData
*target
)
2239 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2241 /* Hold on to the original head of the target ban list
2242 to avoid comparing source bans with source bans. */
2243 tFront
= target
->bans
;
2245 /* Perform a totally expensive O(n*m) merge, ick. */
2246 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2248 /* Flag to track whether the ban's been moved
2249 to the destination yet. */
2252 /* Possible to assert (sbData->prev == NULL) here. */
2253 sNext
= sbData
->next
;
2255 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2257 tNext
= tbData
->next
;
2259 /* Perform two comparisons between each source
2260 and target ban, conflicts are resolved by
2261 keeping the broader ban and copying the later
2262 expiration and triggered time. */
2263 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2265 /* There is a broader ban in the target channel that
2266 overrides one in the source channel; remove the
2267 source ban and break. */
2268 if(sbData
->expires
> tbData
->expires
)
2269 tbData
->expires
= sbData
->expires
;
2270 if(sbData
->triggered
> tbData
->triggered
)
2271 tbData
->triggered
= sbData
->triggered
;
2272 del_channel_ban(sbData
);
2275 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2277 /* There is a broader ban in the source channel that
2278 overrides one in the target channel; remove the
2279 target ban, fall through and move the source over. */
2280 if(tbData
->expires
> sbData
->expires
)
2281 sbData
->expires
= tbData
->expires
;
2282 if(tbData
->triggered
> sbData
->triggered
)
2283 sbData
->triggered
= tbData
->triggered
;
2284 if(tbData
== tFront
)
2286 del_channel_ban(tbData
);
2289 /* Source bans can override multiple target bans, so
2290 we allow a source to run through this loop multiple
2291 times, but we can only move it once. */
2296 /* Remove the source ban from the source ban list. */
2298 sbData
->next
->prev
= sbData
->prev
;
2300 /* Modify the source ban's associated channel. */
2301 sbData
->channel
= target
;
2303 /* Insert the ban into the target channel's linked list. */
2304 sbData
->prev
= NULL
;
2305 sbData
->next
= target
->bans
;
2308 target
->bans
->prev
= sbData
;
2309 target
->bans
= sbData
;
2311 /* Update the user counts for the target channel. */
2316 /* Possible to assert (source->bans == NULL) here. */
2317 source
->bans
= NULL
;
2321 merge_data(struct chanData
*source
, struct chanData
*target
)
2323 if(source
->visited
> target
->visited
)
2324 target
->visited
= source
->visited
;
2328 merge_channel(struct chanData
*source
, struct chanData
*target
)
2330 merge_users(source
, target
);
2331 merge_bans(source
, target
);
2332 merge_data(source
, target
);
2335 static CHANSERV_FUNC(cmd_merge
)
2337 struct userData
*target_user
;
2338 struct chanNode
*target
;
2339 char reason
[MAXLEN
];
2343 /* Make sure the target channel exists and is registered to the user
2344 performing the command. */
2345 if(!(target
= GetChannel(argv
[1])))
2347 reply("MSG_INVALID_CHANNEL");
2351 if(!target
->channel_info
)
2353 reply("CSMSG_NOT_REGISTERED", target
->name
);
2357 if(IsProtected(channel
->channel_info
))
2359 reply("CSMSG_MERGE_NODELETE");
2363 if(IsSuspended(target
->channel_info
))
2365 reply("CSMSG_MERGE_SUSPENDED");
2369 if(channel
== target
)
2371 reply("CSMSG_MERGE_SELF");
2375 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2376 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2378 reply("CSMSG_MERGE_NOT_OWNER");
2382 /* Merge the channel structures and associated data. */
2383 merge_channel(channel
->channel_info
, target
->channel_info
);
2384 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2385 unregister_channel(channel
->channel_info
, reason
);
2386 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2390 static CHANSERV_FUNC(cmd_opchan
)
2392 struct mod_chanmode change
;
2393 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2395 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2398 channel
->channel_info
->may_opchan
= 0;
2399 mod_chanmode_init(&change
);
2401 change
.args
[0].mode
= MODE_CHANOP
;
2402 change
.args
[0].u
.member
= GetUserMode(channel
, chanserv
);
2403 mod_chanmode_announce(chanserv
, channel
, &change
);
2404 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2408 static CHANSERV_FUNC(cmd_adduser
)
2410 struct userData
*actee
;
2411 struct userData
*actor
;
2412 struct handle_info
*handle
;
2413 unsigned short access
;
2417 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2419 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2423 access
= user_level_from_name(argv
[2], UL_OWNER
);
2426 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2430 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2431 if(actor
->access
<= access
)
2433 reply("CSMSG_NO_BUMP_ACCESS");
2437 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2439 // 'kevin must first authenticate with AuthServ.' is sent to user
2440 struct userNode
*unode
;
2441 unode
= GetUserH(argv
[1]); /* find user struct by nick */
2444 if(find_adduser_pending(channel
, unode
)) {
2445 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel
->name
);
2448 if(IsInChannel(channel
, unode
)) {
2449 reply("CSMSG_ADDUSER_PENDING");
2450 add_adduser_pending(channel
, unode
, access
);
2451 send_message_type(1,unode
, chanserv
, "CSMSG_ADDUSER_PENDING_TARGET", user
->nick
, channel
->name
);
2453 /* this results in user must auth AND not in chan errors. too confusing..
2455 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2463 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2465 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, user_level_name_from_level(actee
->access
));
2469 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2470 scan_user_presence(actee
, NULL
);
2471 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, user_level_name_from_level(access
), access
);
2475 static CHANSERV_FUNC(cmd_clvl
)
2477 struct handle_info
*handle
;
2478 struct userData
*victim
;
2479 struct userData
*actor
;
2480 unsigned short new_access
;
2481 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2485 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2487 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2490 if(handle
== user
->handle_info
&& !privileged
)
2492 reply("CSMSG_NO_SELF_CLVL");
2496 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2498 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2502 if(actor
->access
<= victim
->access
&& !privileged
)
2504 reply("MSG_USER_OUTRANKED", handle
->handle
);
2508 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2512 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2516 if(new_access
>= actor
->access
&& !privileged
)
2518 reply("CSMSG_NO_BUMP_ACCESS");
2522 victim
->access
= new_access
;
2523 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, user_level_name_from_level(new_access
), new_access
, channel
->name
);
2527 static CHANSERV_FUNC(cmd_deluser
)
2529 struct handle_info
*handle
;
2530 struct userData
*victim
;
2531 struct userData
*actor
;
2532 unsigned short access
;
2537 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2539 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2542 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2544 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2550 access
= user_level_from_name(argv
[1], UL_OWNER
);
2553 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2556 if(access
!= victim
->access
)
2558 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, user_level_name_from_level(victim
->access
), argv
[1]);
2564 access
= victim
->access
;
2567 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2569 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2573 chan_name
= strdup(channel
->name
);
2574 del_channel_user(victim
, 1);
2575 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2581 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2583 struct userData
*actor
, *uData
, *next
;
2585 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2587 if(min_access
> max_access
)
2589 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2593 if((actor
->access
<= max_access
) && !IsHelping(user
))
2595 reply("CSMSG_NO_ACCESS");
2599 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2603 if((uData
->access
>= min_access
)
2604 && (uData
->access
<= max_access
)
2605 && match_ircglob(uData
->handle
->handle
, mask
))
2606 del_channel_user(uData
, 1);
2609 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2613 static CHANSERV_FUNC(cmd_mdelowner
)
2615 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2618 static CHANSERV_FUNC(cmd_mdelcoowner
)
2620 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2623 static CHANSERV_FUNC(cmd_mdelmanager
)
2625 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2628 static CHANSERV_FUNC(cmd_mdelop
)
2630 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2633 static CHANSERV_FUNC(cmd_mdelpeon
)
2635 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2638 static CHANSERV_FUNC(cmd_mdelhalfop
)
2640 return cmd_mdel_user(user
, channel
, UL_HALFOP
, UL_HALFOP
, argv
[1], cmd
);
2645 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2647 struct banData
*bData
, *next
;
2648 char interval
[INTERVALLEN
];
2653 limit
= now
- duration
;
2654 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2658 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2661 del_channel_ban(bData
);
2665 intervalString(interval
, duration
, user
->handle_info
);
2666 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2671 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2673 struct userData
*actor
, *uData
, *next
;
2674 char interval
[INTERVALLEN
];
2678 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2679 if(min_access
> max_access
)
2681 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2685 if((actor
->access
<= max_access
) && !IsHelping(user
))
2687 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2692 limit
= now
- duration
;
2693 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2697 if((uData
->seen
> limit
) || uData
->present
)
2700 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2701 || (!max_access
&& (uData
->access
< actor
->access
)))
2703 del_channel_user(uData
, 1);
2711 max_access
= (actor
->access
> UL_OWNER
) ? UL_OWNER
: (actor
->access
- 1);
2713 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2717 static CHANSERV_FUNC(cmd_trim
)
2719 unsigned long duration
;
2720 unsigned short min_level
, max_level
;
2724 duration
= ParseInterval(argv
[2]);
2727 reply("CSMSG_CANNOT_TRIM");
2731 if(!irccasecmp(argv
[1], "bans"))
2733 cmd_trim_bans(user
, channel
, duration
);
2736 else if(!irccasecmp(argv
[1], "users"))
2738 cmd_trim_users(user
, channel
, 0, 0, duration
);
2741 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2743 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2746 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2748 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2753 reply("CSMSG_INVALID_TRIM", argv
[1]);
2758 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2759 to the user. cmd_all takes advantage of this. */
2760 static CHANSERV_FUNC(cmd_up
)
2762 struct mod_chanmode change
;
2763 struct userData
*uData
;
2766 mod_chanmode_init(&change
);
2768 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2769 if(!change
.args
[0].u
.member
)
2772 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2776 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2780 reply("CSMSG_GODMODE_UP", argv
[0]);
2783 else if(uData
->access
>= UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
2785 change
.args
[0].mode
= MODE_CHANOP
;
2786 errmsg
= "CSMSG_ALREADY_OPPED";
2788 else if(uData
->access
>= UL_HALFOP
/*channel->channel_info->lvlOpts[lvlGiveHalfOps]*/)
2790 change
.args
[0].mode
= MODE_HALFOP
;
2791 errmsg
= "CSMSG_ALREADY_HALFOPPED";
2793 else if(uData
->access
>= UL_PEON
&& (channel
->channel_info
->chOpts
[chVoice
] == 'p' || channel
->channel_info
->chOpts
[chVoice
] == 'a'))
2795 change
.args
[0].mode
= MODE_VOICE
;
2796 errmsg
= "CSMSG_ALREADY_VOICED";
2801 reply("CSMSG_NO_ACCESS");
2804 change
.args
[0].mode
&= ~change
.args
[0].u
.member
->modes
;
2805 if(!change
.args
[0].mode
)
2808 reply(errmsg
, channel
->name
);
2811 modcmd_chanmode_announce(&change
);
2815 static CHANSERV_FUNC(cmd_down
)
2817 struct mod_chanmode change
;
2819 mod_chanmode_init(&change
);
2821 change
.args
[0].u
.member
= GetUserMode(channel
, user
);
2822 if(!change
.args
[0].u
.member
)
2825 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2829 if(!change
.args
[0].u
.member
->modes
)
2832 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2836 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].u
.member
->modes
;
2837 modcmd_chanmode_announce(&change
);
2841 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
)
2843 struct userData
*cList
;
2845 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2847 if(IsSuspended(cList
->channel
)
2848 || IsUserSuspended(cList
)
2849 || !GetUserMode(cList
->channel
->channel
, user
))
2852 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2858 static CHANSERV_FUNC(cmd_upall
)
2860 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2863 static CHANSERV_FUNC(cmd_downall
)
2865 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2868 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2869 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2872 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
)
2874 unsigned int ii
, valid
;
2875 struct userNode
*victim
;
2876 struct mod_chanmode
*change
;
2878 change
= mod_chanmode_alloc(argc
- 1);
2880 for(ii
=valid
=0; ++ii
< argc
; )
2882 if(!(victim
= GetUserH(argv
[ii
])))
2884 change
->args
[valid
].mode
= mode
;
2885 change
->args
[valid
].u
.member
= GetUserMode(channel
, victim
);
2886 if(!change
->args
[valid
].u
.member
)
2888 if(validate
&& !validate(user
, channel
, victim
))
2893 change
->argc
= valid
;
2894 if(valid
< (argc
-1))
2895 reply("CSMSG_PROCESS_FAILED");
2898 modcmd_chanmode_announce(change
);
2899 reply(action
, channel
->name
);
2901 mod_chanmode_free(change
);
2905 static CHANSERV_FUNC(cmd_op
)
2907 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2910 static CHANSERV_FUNC(cmd_hop
)
2912 return modify_users(CSFUNC_ARGS
, validate_halfop
, MODE_HALFOP
, "CSMSG_HALFOPPED_USERS");
2915 static CHANSERV_FUNC(cmd_deop
)
2917 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2920 static CHANSERV_FUNC(cmd_dehop
)
2922 return modify_users(CSFUNC_ARGS
, validate_dehop
, MODE_REMOVE
|MODE_HALFOP
, "CSMSG_DEHALFOPPED_USERS");
2925 static CHANSERV_FUNC(cmd_voice
)
2927 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2930 static CHANSERV_FUNC(cmd_devoice
)
2932 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2936 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2942 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2944 struct modeNode
*mn
= channel
->members
.list
[ii
];
2946 if(IsService(mn
->user
))
2949 if(!user_matches_glob(mn
->user
, ban
, 1))
2952 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2956 victims
[(*victimCount
)++] = mn
;
2962 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2964 struct userNode
*victim
;
2965 struct modeNode
**victims
;
2966 unsigned int offset
, n
, victimCount
, duration
= 0;
2967 char *reason
= "Bye.", *ban
, *name
;
2968 char interval
[INTERVALLEN
];
2970 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2971 REQUIRE_PARAMS(offset
);
2974 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2975 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2977 /* Truncate the reason to a length of TOPICLEN, as
2978 the ircd does; however, leave room for an ellipsis
2979 and the kicker's nick. */
2980 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2984 if((victim
= GetUserH(argv
[1])))
2986 victims
= alloca(sizeof(victims
[0]));
2987 victims
[0] = GetUserMode(channel
, victim
);
2988 /* XXX: The comparison with ACTION_KICK is just because all
2989 * other actions can work on users outside the channel, and we
2990 * want to allow those (e.g. unbans) in that case. If we add
2991 * some other ejection action for in-channel users, change
2993 victimCount
= victims
[0] ? 1 : 0;
2995 if(IsService(victim
))
2997 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
3001 if((action
== ACTION_KICK
) && !victimCount
)
3003 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
3007 if(protect_user(victim
, user
, channel
->channel_info
))
3009 reply("CSMSG_USER_PROTECTED", victim
->nick
);
3013 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
3014 name
= victim
->nick
;
3018 if(!is_ircmask(argv
[1]))
3020 reply("MSG_NICK_UNKNOWN", argv
[1]);
3024 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
3026 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
3028 reply("CSMSG_MASK_PROTECTED", argv
[1]);
3031 /* We dont actually want a victem count if were banning a mask manually, IMO -Rubin*/
3033 victimCount
= 0; /* Dont deop etc ppl who match this */
3035 #ifdef entropy_lameness
3036 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
3038 reply("CSMSG_LAME_MASK", argv
[1]);
3043 if((action
== ACTION_KICK
) && (victimCount
== 0))
3045 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
3049 name
= ban
= strdup(argv
[1]);
3052 /* Truncate the ban in place if necessary; we must ensure
3053 that 'ban' is a valid ban mask before sanitizing it. */
3054 sanitize_ircmask(ban
);
3056 if(action
& ACTION_ADD_BAN
)
3058 struct banData
*bData
, *next
;
3060 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
3062 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
3067 if(action
& ACTION_ADD_TIMED_BAN
)
3069 duration
= ParseInterval(argv
[2]);
3073 reply("CSMSG_DURATION_TOO_LOW");
3077 else if(duration
> (86400 * 365 * 2))
3079 reply("CSMSG_DURATION_TOO_HIGH");
3085 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
3087 if(match_ircglobs(bData
->mask
, ban
))
3089 int exact
= !irccasecmp(bData
->mask
, ban
);
3091 /* The ban is redundant; there is already a ban
3092 with the same effect in place. */
3096 free(bData
->reason
);
3097 bData
->reason
= strdup(reason
);
3098 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
3100 reply("CSMSG_REASON_CHANGE", ban
);
3104 if(exact
&& bData
->expires
)
3108 /* If the ban matches an existing one exactly,
3109 extend the expiration time if the provided
3110 duration is longer. */
3111 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
3113 bData
->expires
= now
+ duration
;
3124 /* Delete the expiration timeq entry and
3125 requeue if necessary. */
3126 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
3129 timeq_add(bData
->expires
, expire_ban
, bData
);
3133 /* automated kickban */
3136 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
3138 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3144 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3151 if(match_ircglobs(ban
, bData
->mask
))
3153 /* The ban we are adding makes previously existing
3154 bans redundant; silently remove them. */
3155 del_channel_ban(bData
);
3159 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
);
3161 name
= ban
= strdup(bData
->mask
);
3165 /* WHAT DOES THIS DO?? -Rubin */
3166 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
3168 extern const char *hidden_host_suffix
;
3169 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
3171 unsigned int l1
, l2
;
3174 l2
= strlen(old_name
);
3177 if(irccasecmp(ban
+ l1
- l2
, old_name
))
3179 new_mask
= malloc(MAXLEN
);
3180 sprintf(new_mask
, "%.*s%s", (int)(l1
-l2
), ban
, hidden_host_suffix
);
3182 name
= ban
= new_mask
;
3187 if(action
& ACTION_BAN
)
3189 unsigned int exists
;
3190 struct mod_chanmode
*change
;
3192 if(channel
->banlist
.used
>= MAXBANS
)
3195 reply("CSMSG_BANLIST_FULL", channel
->name
);
3200 exists
= ChannelBanExists(channel
, ban
);
3201 change
= mod_chanmode_alloc(victimCount
+ 1);
3202 for(n
= 0; n
< victimCount
; ++n
)
3204 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
;
3205 change
->args
[n
].u
.member
= victims
[n
];
3209 change
->args
[n
].mode
= MODE_BAN
;
3210 change
->args
[n
++].u
.hostmask
= ban
;
3214 modcmd_chanmode_announce(change
);
3216 mod_chanmode_announce(chanserv
, channel
, change
);
3217 mod_chanmode_free(change
);
3219 if(exists
&& (action
== ACTION_BAN
))
3222 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
3228 if(action
& ACTION_KICK
)
3230 char kick_reason
[MAXLEN
];
3231 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
3233 for(n
= 0; n
< victimCount
; n
++)
3234 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
3239 /* No response, since it was automated. */
3241 else if(action
& ACTION_ADD_BAN
)
3244 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
3246 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
3248 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
3249 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
3250 else if(action
& ACTION_BAN
)
3251 reply("CSMSG_BAN_DONE", name
, channel
->name
);
3252 else if(action
& ACTION_KICK
&& victimCount
)
3253 reply("CSMSG_KICK_DONE", name
, channel
->name
);
3259 static CHANSERV_FUNC(cmd_kickban
)
3261 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
3264 static CHANSERV_FUNC(cmd_kick
)
3266 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
3269 static CHANSERV_FUNC(cmd_ban
)
3271 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
3274 static CHANSERV_FUNC(cmd_addban
)
3276 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
3279 static CHANSERV_FUNC(cmd_addtimedban
)
3281 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
3284 static struct mod_chanmode
*
3285 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
3287 struct mod_chanmode
*change
;
3288 unsigned char *match
;
3289 unsigned int ii
, count
;
3291 match
= alloca(bans
->used
);
3294 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3296 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3303 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3305 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3312 change
= mod_chanmode_alloc(count
);
3313 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3317 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3318 change
->args
[count
++].u
.hostmask
= strdup(bans
->list
[ii
]->ban
);
3320 assert(count
== change
->argc
);
3325 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3327 struct userNode
*actee
;
3333 /* may want to allow a comma delimited list of users... */
3334 if(!(actee
= GetUserH(argv
[1])))
3336 if(!is_ircmask(argv
[1]))
3338 reply("MSG_NICK_UNKNOWN", argv
[1]);
3342 mask
= strdup(argv
[1]);
3345 /* We don't sanitize the mask here because ircu
3347 if(action
& ACTION_UNBAN
)
3349 struct mod_chanmode
*change
;
3350 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3355 modcmd_chanmode_announce(change
);
3356 for(ii
= 0; ii
< change
->argc
; ++ii
)
3357 free((char*)change
->args
[ii
].u
.hostmask
);
3358 mod_chanmode_free(change
);
3363 if(action
& ACTION_DEL_BAN
)
3365 struct banData
*ban
, *next
;
3367 ban
= channel
->channel_info
->bans
;
3371 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3374 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3379 del_channel_ban(ban
);
3386 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3388 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3394 static CHANSERV_FUNC(cmd_unban
)
3396 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3399 static CHANSERV_FUNC(cmd_delban
)
3401 /* it doesn't necessarily have to remove the channel ban - may want
3402 to make that an option. */
3403 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3406 static CHANSERV_FUNC(cmd_unbanme
)
3408 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3409 long flags
= ACTION_UNBAN
;
3411 /* remove permanent bans if the user has the proper access. */
3412 if(uData
->access
>= UL_MANAGER
)
3413 flags
|= ACTION_DEL_BAN
;
3415 argv
[1] = user
->nick
;
3416 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3419 static CHANSERV_FUNC(cmd_unbanall
)
3421 struct mod_chanmode
*change
;
3424 if(!channel
->banlist
.used
)
3426 reply("CSMSG_NO_BANS", channel
->name
);
3430 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3431 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3433 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3434 change
->args
[ii
].u
.hostmask
= strdup(channel
->banlist
.list
[ii
]->ban
);
3436 modcmd_chanmode_announce(change
);
3437 for(ii
= 0; ii
< change
->argc
; ++ii
)
3438 free((char*)change
->args
[ii
].u
.hostmask
);
3439 mod_chanmode_free(change
);
3440 reply("CSMSG_BANS_REMOVED", channel
->name
);
3444 static CHANSERV_FUNC(cmd_open
)
3446 struct mod_chanmode
*change
;
3449 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3451 change
= mod_chanmode_alloc(0);
3452 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3453 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3454 && channel
->channel_info
->modes
.modes_set
)
3455 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3456 modcmd_chanmode_announce(change
);
3457 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3458 for(ii
= 0; ii
< change
->argc
; ++ii
)
3459 free((char*)change
->args
[ii
].u
.hostmask
);
3460 mod_chanmode_free(change
);
3464 static CHANSERV_FUNC(cmd_myaccess
)
3466 static struct string_buffer sbuf
;
3467 struct handle_info
*target_handle
;
3468 struct userData
*uData
;
3471 target_handle
= user
->handle_info
;
3472 else if(!IsHelping(user
))
3474 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3477 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3480 if(!target_handle
->channels
)
3482 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3486 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3487 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3489 struct chanData
*cData
= uData
->channel
;
3491 if(uData
->access
> UL_OWNER
)
3493 if(IsProtected(cData
)
3494 && (target_handle
!= user
->handle_info
)
3495 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3498 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3499 if(uData
->flags
== USER_AUTO_OP
)
3500 string_buffer_append(&sbuf
, ',');
3501 if(IsUserSuspended(uData
))
3502 string_buffer_append(&sbuf
, 's');
3503 if(IsUserAutoOp(uData
))
3505 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
3506 string_buffer_append(&sbuf
, 'o');
3507 else if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
3508 string_buffer_append(&sbuf
, 'h');
3509 else if(uData
->access
>= UL_PEON
/*cData->lvlOpts[lvlGiveVoice]*/)
3510 string_buffer_append(&sbuf
, 'v');
3512 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3513 string_buffer_append(&sbuf
, 'i');
3515 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3517 string_buffer_append_string(&sbuf
, ")]");
3518 string_buffer_append(&sbuf
, '\0');
3519 send_message_type(4, user
, cmd
->parent
->bot
, "%s", sbuf
.list
);
3525 static CHANSERV_FUNC(cmd_access
)
3527 struct userNode
*target
;
3528 struct handle_info
*target_handle
;
3529 struct userData
*uData
;
3531 char prefix
[MAXLEN
];
3536 target_handle
= target
->handle_info
;
3538 else if((target
= GetUserH(argv
[1])))
3540 target_handle
= target
->handle_info
;
3542 else if(argv
[1][0] == '*')
3544 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3546 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3552 reply("MSG_NICK_UNKNOWN", argv
[1]);
3556 assert(target
|| target_handle
);
3558 if(target
== chanserv
)
3560 reply("CSMSG_IS_CHANSERV");
3568 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3573 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3576 reply("MSG_AUTHENTICATE");
3582 const char *epithet
= NULL
, *type
= NULL
;
3585 epithet
= chanserv_conf
.irc_operator_epithet
;
3588 else if(IsNetworkHelper(target
))
3590 epithet
= chanserv_conf
.network_helper_epithet
;
3591 type
= "network helper";
3593 else if(IsSupportHelper(target
))
3595 epithet
= chanserv_conf
.support_helper_epithet
;
3596 type
= "support helper";
3600 if(target_handle
->epithet
)
3601 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3603 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3605 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3609 sprintf(prefix
, "%s", target_handle
->handle
);
3612 if(!channel
->channel_info
)
3614 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3618 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3619 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3620 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3622 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, user_level_name_from_level(uData
->access
), uData
->access
, channel
->name
);
3623 /* To prevent possible information leaks, only show infolines
3624 * if the requestor is in the channel or it's their own
3626 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3628 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3630 /* Likewise, only say it's suspended if the user has active
3631 * access in that channel or it's their own entry. */
3632 if(IsUserSuspended(uData
)
3633 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3634 || (user
->handle_info
== uData
->handle
)))
3636 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3641 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3647 /* This is never used... */
3649 zoot_list(struct listData
*list
)
3651 struct userData
*uData
;
3652 unsigned int start
, curr
, highest
, lowest
;
3653 struct helpfile_table tmp_table
;
3654 const char **temp
, *msg
;
3656 if(list
->table
.length
== 1)
3659 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
);
3661 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
));
3662 msg
= user_find_message(list
->user
, "MSG_NONE");
3663 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3665 tmp_table
.width
= list
->table
.width
;
3666 tmp_table
.flags
= list
->table
.flags
;
3667 list
->table
.contents
[0][0] = " ";
3668 highest
= list
->highest
;
3669 if(list
->lowest
!= 0)
3670 lowest
= list
->lowest
;
3671 else if(highest
< 100)
3674 lowest
= highest
- 100;
3675 for(start
= curr
= 1; curr
< list
->table
.length
; )
3677 uData
= list
->users
[curr
-1];
3678 list
->table
.contents
[curr
++][0] = " ";
3679 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3682 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
);
3684 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
));
3685 temp
= list
->table
.contents
[--start
];
3686 list
->table
.contents
[start
] = list
->table
.contents
[0];
3687 tmp_table
.contents
= list
->table
.contents
+ start
;
3688 tmp_table
.length
= curr
- start
;
3689 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3690 list
->table
.contents
[start
] = temp
;
3692 highest
= lowest
- 1;
3693 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3699 def_list(struct listData
*list
)
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 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3707 if(list
->table
.length
== 1)
3709 msg
= user_find_message(list
->user
, "MSG_NONE");
3710 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3715 userData_access_comp(const void *arg_a
, const void *arg_b
)
3717 const struct userData
*a
= *(struct userData
**)arg_a
;
3718 const struct userData
*b
= *(struct userData
**)arg_b
;
3720 if(a
->access
!= b
->access
)
3721 res
= b
->access
- a
->access
;
3723 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3728 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3730 void (*send_list
)(struct listData
*);
3731 struct userData
*uData
;
3732 struct listData lData
;
3733 unsigned int matches
;
3737 lData
.bot
= cmd
->parent
->bot
;
3738 lData
.channel
= channel
;
3739 lData
.lowest
= lowest
;
3740 lData
.highest
= highest
;
3741 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3742 send_list
= def_list
;
3743 /* What does the following line do exactly?? */
3744 (void)zoot_list
; /* since it doesn't show user levels */
3746 /* this does nothing!! -rubin
3747 if(user->handle_info)
3749 switch(user->handle_info->userlist_style)
3751 case HI_STYLE_DEF: send_list = def_list; break;
3752 case HI_STYLE_ZOOT: send_list = def_list; break;
3757 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3759 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3761 if((uData
->access
< lowest
)
3762 || (uData
->access
> highest
)
3763 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3765 lData
.users
[matches
++] = uData
;
3767 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3769 lData
.table
.length
= matches
+1;
3770 lData
.table
.width
= 5;
3771 lData
.table
.flags
= TABLE_NO_FREE
;
3772 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3773 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3774 lData
.table
.contents
[0] = ary
;
3778 ary
[3] = "Last Seen";
3780 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3782 struct userData
*uData
= lData
.users
[matches
-1];
3783 char seen
[INTERVALLEN
];
3785 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3786 lData
.table
.contents
[matches
] = ary
;
3787 /* ary[0] = strtab(uData->access);*/
3788 ary
[0] = user_level_name_from_level(uData
->access
);
3789 ary
[1] = strtab(uData
->access
);
3790 ary
[2] = uData
->handle
->handle
;
3793 else if(!uData
->seen
)
3796 ary
[3] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3797 ary
[3] = strdup(ary
[3]);
3798 if(IsUserSuspended(uData
))
3799 ary
[4] = "Suspended";
3800 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3801 ary
[4] = "Vacation";
3806 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3808 free((char*)lData
.table
.contents
[matches
][3]);
3809 free(lData
.table
.contents
[matches
]);
3811 free(lData
.table
.contents
[0]);
3812 free(lData
.table
.contents
);
3816 static CHANSERV_FUNC(cmd_pending
)
3818 struct adduserPending
*ap
;
3819 for(ap
= adduser_pendings
;ap
;ap
= ap
->next
)
3820 reply("CSMSG_ADDUSER_PENDING_LIST", ap
->channel
->name
, ap
->user
->nick
);
3824 static CHANSERV_FUNC(cmd_users
)
3826 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3829 static CHANSERV_FUNC(cmd_wlist
)
3831 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3834 static CHANSERV_FUNC(cmd_clist
)
3836 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3839 static CHANSERV_FUNC(cmd_mlist
)
3841 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3844 static CHANSERV_FUNC(cmd_olist
)
3846 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3849 static CHANSERV_FUNC(cmd_hlist
)
3851 return cmd_list_users(CSFUNC_ARGS
, UL_HALFOP
, UL_OP
-1);
3854 static CHANSERV_FUNC(cmd_plist
)
3856 return cmd_list_users(CSFUNC_ARGS
, 1, UL_HALFOP
-1);
3859 static CHANSERV_FUNC(cmd_bans
)
3861 struct helpfile_table tbl
;
3862 unsigned int matches
= 0, timed
= 0, ii
;
3863 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3864 const char *msg_never
, *triggered
, *expires
;
3865 struct banData
*ban
, **bans
;
3872 reply("CSMSG_BANS_HEADER", channel
->name
);
3873 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3875 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3877 if(search
&& !match_ircglobs(search
, ban
->mask
))
3879 bans
[matches
++] = ban
;
3884 tbl
.length
= matches
+ 1;
3885 tbl
.width
= 4 + timed
;
3887 tbl
.flags
= TABLE_NO_FREE
;
3888 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3889 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3890 tbl
.contents
[0][0] = "Mask";
3891 tbl
.contents
[0][1] = "Set By";
3892 tbl
.contents
[0][2] = "Triggered";
3895 tbl
.contents
[0][3] = "Expires";
3896 tbl
.contents
[0][4] = "Reason";
3899 tbl
.contents
[0][3] = "Reason";
3902 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3904 free(tbl
.contents
[0]);
3909 msg_never
= user_find_message(user
, "MSG_NEVER");
3910 for(ii
= 0; ii
< matches
; )
3916 else if(ban
->expires
)
3917 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3919 expires
= msg_never
;
3922 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3924 triggered
= msg_never
;
3926 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3927 tbl
.contents
[ii
][0] = ban
->mask
;
3928 tbl
.contents
[ii
][1] = ban
->owner
;
3929 tbl
.contents
[ii
][2] = strdup(triggered
);
3932 tbl
.contents
[ii
][3] = strdup(expires
);
3933 tbl
.contents
[ii
][4] = ban
->reason
;
3936 tbl
.contents
[ii
][3] = ban
->reason
;
3938 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3939 /* reply("MSG_MATCH_COUNT", matches); */
3940 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3942 free((char*)tbl
.contents
[ii
][2]);
3944 free((char*)tbl
.contents
[ii
][3]);
3945 free(tbl
.contents
[ii
]);
3947 free(tbl
.contents
[0]);
3954 * return + if the user does NOT have the right to set the topic, and
3955 * the topic is changed.
3958 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3960 struct chanData
*cData
= channel
->channel_info
;
3961 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3963 else if(cData
->topic
)
3964 return irccasecmp(new_topic
, cData
->topic
);
3971 * Makes a givin topic fit into a givin topic mask and returns
3974 * topic_mask - the mask to conform to
3975 * topic - the topic to make conform
3976 * new_topic - the pre-allocated char* to put the new topic into
3978 * modifies: new_topic
3981 conform_topic(char* topic_mask
, char* topic
, char *new_topic
)
3983 //char *topic_mask = cData->topic_mask;
3985 int pos
=0, starpos
=-1, dpos
=0, len
;
3987 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3994 strcpy(new_topic
, "");
3997 len
= strlen(topic
);
3998 if((dpos
+ len
) > TOPICLEN
)
3999 len
= TOPICLEN
+ 1 - dpos
;
4000 memcpy(new_topic
+dpos
, topic
, len
);
4004 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
4005 default: new_topic
[dpos
++] = tchar
; break;
4008 if((dpos
> TOPICLEN
) || tchar
)
4010 strcpy(new_topic
, "");
4013 new_topic
[dpos
] = 0;
4017 static CHANSERV_FUNC(cmd_topic
)
4019 struct chanData
*cData
;
4022 cData
= channel
->channel_info
;
4027 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
4028 reply("CSMSG_TOPIC_SET", cData
->topic
);
4032 reply("CSMSG_NO_TOPIC", channel
->name
);
4036 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4037 /* If they say "!topic *", use an empty topic. */
4038 if((topic
[0] == '*') && (topic
[1] == 0))
4041 if(bad_topic(channel
, user
, topic
))
4043 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4048 /* If there is a topicmask set, and the new topic doesnt match, make it */
4049 if(cData
->topic_mask
&& !match_ircglob(topic
, cData
->topic_mask
))
4051 char *topic_mask
= cData
->topic_mask
;
4052 char new_topic
[TOPICLEN
+1];
4054 /* make a new topic fitting mask */
4055 conform_topic(topic_mask
, topic
, new_topic
);
4058 /* Topic couldnt fit into mask, was too long */
4059 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
4060 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
4063 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
4065 else /* No mask set, just set the topic */
4066 SetChannelTopic(channel
, chanserv
, topic
, 1);
4069 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
4071 /* Grab the topic and save it as the default topic. */
4073 cData
->topic
= strdup(channel
->topic
);
4079 static CHANSERV_FUNC(cmd_mode
)
4081 struct mod_chanmode
*change
;
4085 change
= &channel
->channel_info
->modes
;
4086 if(change
->modes_set
|| change
->modes_clear
) {
4087 modcmd_chanmode_announce(change
);
4088 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
4090 reply("CSMSG_NO_MODES", channel
->name
);
4094 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
);
4097 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
4101 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
4102 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
4105 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4106 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
4110 modcmd_chanmode_announce(change
);
4111 mod_chanmode_free(change
);
4112 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
4116 static CHANSERV_FUNC(cmd_invite
)
4118 struct userData
*uData
;
4119 struct userNode
*invite
;
4121 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4125 if(!(invite
= GetUserH(argv
[1])))
4127 reply("MSG_NICK_UNKNOWN", argv
[1]);
4134 if(GetUserMode(channel
, invite
))
4136 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
4144 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4145 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
4148 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
4150 irc_invite(chanserv
, invite
, channel
);
4152 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
4157 static CHANSERV_FUNC(cmd_inviteme
)
4159 if(GetUserMode(channel
, user
))
4161 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
4164 if(channel
->channel_info
4165 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
4167 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
4170 irc_invite(cmd
->parent
->bot
, user
, channel
);
4175 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
4178 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
4180 /* We display things based on two dimensions:
4181 * - Issue time: present or absent
4182 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4183 * (in order of precedence, so something both expired and revoked
4184 * only counts as revoked)
4186 combo
= (suspended
->issued
? 4 : 0)
4187 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
4189 case 0: /* no issue time, indefinite expiration */
4190 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
4192 case 1: /* no issue time, expires in future */
4193 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
4194 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
4196 case 2: /* no issue time, expired */
4197 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
4198 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
4200 case 3: /* no issue time, revoked */
4201 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
4202 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
4204 case 4: /* issue time set, indefinite expiration */
4205 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4206 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
4208 case 5: /* issue time set, expires in future */
4209 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4210 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
4211 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4213 case 6: /* issue time set, expired */
4214 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4215 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
4216 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4218 case 7: /* issue time set, revoked */
4219 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
4220 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
4221 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
4224 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
4229 static CHANSERV_FUNC(cmd_info
)
4231 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
4232 struct userData
*uData
, *owner
;
4233 struct chanData
*cData
;
4234 struct do_not_register
*dnr
;
4239 cData
= channel
->channel_info
;
4240 reply("CSMSG_CHANNEL_INFO", channel
->name
);
4243 uData
= GetChannelUser(cData
, user
->handle_info
);
4244 if(uData
&& (uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/))
4246 mod_chanmode_format(&cData
->modes
, modes
);
4247 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
4248 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
4251 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
4255 note
= iter_data(it
);
4256 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4259 padding
= PADLEN
- 1 - strlen(iter_key(it
));
4260 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
4263 reply("CSMSG_CHANNEL_MAX", cData
->max
);
4264 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
4265 if(owner
->access
== UL_OWNER
)
4266 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
4267 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
4268 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
4269 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
4270 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
4272 privileged
= IsStaff(user
);
4273 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
4274 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
4276 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
4277 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
4279 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
4281 struct suspended
*suspended
;
4282 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
4283 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
4284 show_suspension_info(cmd
, user
, suspended
);
4286 else if(IsSuspended(cData
))
4288 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
4289 show_suspension_info(cmd
, user
, cData
->suspended
);
4291 reply("CSMSG_CHANNEL_END");
4295 static CHANSERV_FUNC(cmd_netinfo
)
4297 extern time_t boot_time
;
4298 extern unsigned long burst_length
;
4299 char interval
[INTERVALLEN
];
4301 reply("CSMSG_NETWORK_INFO");
4302 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
4303 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
4304 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
4305 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
4306 reply("CSMSG_NETWORK_BANS", banCount
);
4307 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
4308 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
4309 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
4314 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
4316 struct helpfile_table table
;
4318 struct userNode
*user
;
4323 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4324 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
4325 for(nn
=0; nn
<list
->used
; nn
++)
4327 user
= list
->list
[nn
];
4328 if(user
->modes
& skip_flags
)
4332 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
4335 nick
= alloca(strlen(user
->nick
)+3);
4336 sprintf(nick
, "(%s)", user
->nick
);
4340 table
.contents
[table
.length
][0] = nick
;
4343 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
4346 static CHANSERV_FUNC(cmd_ircops
)
4348 reply("CSMSG_STAFF_OPERS");
4349 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
4353 static CHANSERV_FUNC(cmd_helpers
)
4355 reply("CSMSG_STAFF_HELPERS");
4356 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4360 static CHANSERV_FUNC(cmd_staff
)
4362 reply("CSMSG_NETWORK_STAFF");
4363 cmd_ircops(CSFUNC_ARGS
);
4364 cmd_helpers(CSFUNC_ARGS
);
4368 static CHANSERV_FUNC(cmd_peek
)
4370 struct modeNode
*mn
;
4371 char modes
[MODELEN
];
4373 struct helpfile_table table
;
4375 irc_make_chanmode(channel
, modes
);
4377 reply("CSMSG_PEEK_INFO", channel
->name
);
4379 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4380 reply("CSMSG_PEEK_MODES", modes
);
4381 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4385 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4386 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4387 for(n
= 0; n
< channel
->members
.used
; n
++)
4389 mn
= channel
->members
.list
[n
];
4390 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4392 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4393 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4398 reply("CSMSG_PEEK_OPS");
4399 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4402 reply("CSMSG_PEEK_NO_OPS");
4403 reply("CSMSG_PEEK_END");
4407 static MODCMD_FUNC(cmd_wipeinfo
)
4409 struct handle_info
*victim
;
4410 struct userData
*ud
, *actor
;
4413 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4414 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4416 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4418 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4421 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4423 reply("MSG_USER_OUTRANKED", victim
->handle
);
4429 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4433 static CHANSERV_FUNC(cmd_resync
)
4435 struct mod_chanmode
*changes
;
4436 struct chanData
*cData
= channel
->channel_info
;
4437 unsigned int ii
, used
;
4439 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4440 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4442 struct modeNode
*mn
= channel
->members
.list
[ii
];
4443 struct userData
*uData
;
4445 if(IsService(mn
->user
))
4448 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4449 if(uData
&& uData
->access
>= UL_OP
/* cData->lvlOpts[lvlGiveOps]*/)
4451 if(!(mn
->modes
& MODE_CHANOP
))
4453 changes
->args
[used
].mode
= MODE_CHANOP
;
4454 changes
->args
[used
++].u
.member
= mn
;
4457 else if(uData
&& uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
4459 if(!(mn
->modes
& MODE_HALFOP
))
4461 changes
->args
[used
].mode
= MODE_HALFOP
;
4462 changes
->args
[used
++].u
.member
= mn
;
4464 if(mn
->modes
& MODE_CHANOP
)
4466 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_CHANOP
);
4467 changes
->args
[used
++].u
.member
= mn
;
4469 if(mn
->modes
& MODE_VOICE
)
4471 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4472 changes
->args
[used
++].u
.member
= mn
;
4475 else if(uData
&& uData
->access
>= UL_PEON
/* cData->lvlOpts[lvlGiveVoice]*/)
4477 if(mn
->modes
& MODE_CHANOP
)
4479 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4480 changes
->args
[used
++].u
.member
= mn
;
4482 if(mn
->modes
& MODE_HALFOP
)
4484 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4485 changes
->args
[used
++].u
.member
= mn
;
4487 if(!(mn
->modes
& MODE_VOICE
))
4489 changes
->args
[used
].mode
= MODE_VOICE
;
4490 changes
->args
[used
++].u
.member
= mn
;
4497 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4498 changes
->args
[used
++].u
.member
= mn
;
4502 changes
->argc
= used
;
4503 modcmd_chanmode_announce(changes
);
4504 mod_chanmode_free(changes
);
4505 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4509 static CHANSERV_FUNC(cmd_seen
)
4511 struct userData
*uData
;
4512 struct handle_info
*handle
;
4513 char seen
[INTERVALLEN
];
4517 if(!irccasecmp(argv
[1], chanserv
->nick
))
4519 reply("CSMSG_IS_CHANSERV");
4523 if(!(handle
= get_handle_info(argv
[1])))
4525 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4529 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4531 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4536 reply("CSMSG_USER_PRESENT", handle
->handle
);
4537 else if(uData
->seen
)
4538 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4540 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4542 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4543 reply("CSMSG_USER_VACATION", handle
->handle
);
4548 static MODCMD_FUNC(cmd_names
)
4550 struct userNode
*targ
;
4551 struct userData
*targData
;
4552 unsigned int ii
, pos
;
4555 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4557 targ
= channel
->members
.list
[ii
]->user
;
4558 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4561 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4564 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4568 if(IsUserSuspended(targData
))
4570 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4573 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4574 reply("CSMSG_END_NAMES", channel
->name
);
4579 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4581 switch(ntype
->visible_type
)
4583 case NOTE_VIS_ALL
: return 1;
4584 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4585 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4590 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4592 struct userData
*uData
;
4594 switch(ntype
->set_access_type
)
4596 case NOTE_SET_CHANNEL_ACCESS
:
4597 if(!user
->handle_info
)
4599 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4601 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4602 case NOTE_SET_CHANNEL_SETTER
:
4603 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4604 case NOTE_SET_PRIVILEGED
: default:
4605 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4609 static CHANSERV_FUNC(cmd_note
)
4611 struct chanData
*cData
;
4613 struct note_type
*ntype
;
4615 cData
= channel
->channel_info
;
4618 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4622 /* If no arguments, show all visible notes for the channel. */
4628 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4630 note
= iter_data(it
);
4631 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4634 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4635 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4638 reply("CSMSG_NOTELIST_END", channel
->name
);
4640 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4642 /* If one argument, show the named note. */
4645 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4646 && note_type_visible_to_user(cData
, note
->type
, user
))
4648 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4650 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4651 && note_type_visible_to_user(NULL
, ntype
, user
))
4653 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4658 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4662 /* Assume they're trying to set a note. */
4666 ntype
= dict_find(note_types
, argv
[1], NULL
);
4669 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4672 else if(note_type_settable_by_user(channel
, ntype
, user
))
4674 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4675 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4676 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4677 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4678 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4680 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4682 /* The note is viewable to staff only, so return 0
4683 to keep the invocation from getting logged (or
4684 regular users can see it in !events). */
4690 reply("CSMSG_NO_ACCESS");
4697 static CHANSERV_FUNC(cmd_delnote
)
4702 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4703 || !note_type_settable_by_user(channel
, note
->type
, user
))
4705 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4708 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4709 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4713 static CHANSERV_FUNC(cmd_events
)
4715 struct logSearch discrim
;
4716 struct logReport report
;
4717 unsigned int matches
, limit
;
4719 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4720 if(limit
< 1 || limit
> 200)
4723 memset(&discrim
, 0, sizeof(discrim
));
4724 discrim
.masks
.bot
= chanserv
;
4725 discrim
.masks
.channel_name
= channel
->name
;
4727 discrim
.masks
.command
= argv
[2];
4728 discrim
.limit
= limit
;
4729 discrim
.max_time
= INT_MAX
;
4730 discrim
.severities
= 1 << LOG_COMMAND
;
4731 report
.reporter
= chanserv
;
4733 reply("CSMSG_EVENT_SEARCH_RESULTS", channel
->name
);
4735 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4737 reply("MSG_MATCH_COUNT", matches
);
4739 reply("MSG_NO_MATCHES");
4743 static CHANSERV_FUNC(cmd_say
)
4749 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4750 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4752 else if(GetUserH(argv
[1]))
4755 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4756 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4760 reply("MSG_NOT_TARGET_NAME");
4766 static CHANSERV_FUNC(cmd_emote
)
4772 /* CTCP is so annoying. */
4773 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4774 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4776 else if(GetUserH(argv
[1]))
4778 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4779 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4783 reply("MSG_NOT_TARGET_NAME");
4789 struct channelList
*
4790 chanserv_support_channels(void)
4792 return &chanserv_conf
.support_channels
;
4795 static CHANSERV_FUNC(cmd_expire
)
4797 int channel_count
= registered_channels
;
4798 expire_channels(NULL
);
4799 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4804 chanserv_expire_suspension(void *data
)
4806 struct suspended
*suspended
= data
;
4807 struct chanNode
*channel
;
4809 if(!suspended
->expires
|| (now
< suspended
->expires
))
4810 suspended
->revoked
= now
;
4811 channel
= suspended
->cData
->channel
;
4812 suspended
->cData
->channel
= channel
;
4813 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4814 if(!IsOffChannel(suspended
->cData
))
4816 struct mod_chanmode change
;
4817 mod_chanmode_init(&change
);
4819 change
.args
[0].mode
= MODE_CHANOP
;
4820 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
4821 mod_chanmode_announce(chanserv
, channel
, &change
);
4825 static CHANSERV_FUNC(cmd_csuspend
)
4827 struct suspended
*suspended
;
4828 char reason
[MAXLEN
];
4829 time_t expiry
, duration
;
4830 struct userData
*uData
;
4834 if(IsProtected(channel
->channel_info
))
4836 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4840 if(argv
[1][0] == '!')
4842 else if(IsSuspended(channel
->channel_info
))
4844 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4845 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4849 if(!strcmp(argv
[1], "0"))
4851 else if((duration
= ParseInterval(argv
[1])))
4852 expiry
= now
+ duration
;
4855 reply("MSG_INVALID_DURATION", argv
[1]);
4859 unsplit_string(argv
+ 2, argc
- 2, reason
);
4861 suspended
= calloc(1, sizeof(*suspended
));
4862 suspended
->revoked
= 0;
4863 suspended
->issued
= now
;
4864 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4865 suspended
->expires
= expiry
;
4866 suspended
->reason
= strdup(reason
);
4867 suspended
->cData
= channel
->channel_info
;
4868 suspended
->previous
= suspended
->cData
->suspended
;
4869 suspended
->cData
->suspended
= suspended
;
4871 if(suspended
->expires
)
4872 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4874 if(IsSuspended(channel
->channel_info
))
4876 suspended
->previous
->revoked
= now
;
4877 if(suspended
->previous
->expires
)
4878 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4879 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4880 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4884 /* Mark all users in channel as absent. */
4885 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4894 /* Mark the channel as suspended, then part. */
4895 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4896 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4897 reply("CSMSG_SUSPENDED", channel
->name
);
4898 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4899 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4904 static CHANSERV_FUNC(cmd_cunsuspend
)
4906 struct suspended
*suspended
;
4907 char message
[MAXLEN
];
4909 if(!IsSuspended(channel
->channel_info
))
4911 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4915 suspended
= channel
->channel_info
->suspended
;
4917 /* Expire the suspension and join ChanServ to the channel. */
4918 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4919 chanserv_expire_suspension(suspended
);
4920 reply("CSMSG_UNSUSPENDED", channel
->name
);
4921 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4922 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4926 typedef struct chanservSearch
4934 unsigned long flags
;
4938 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4941 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4946 search
= malloc(sizeof(struct chanservSearch
));
4947 memset(search
, 0, sizeof(*search
));
4950 for(i
= 0; i
< argc
; i
++)
4952 /* Assume all criteria require arguments. */
4955 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4959 if(!irccasecmp(argv
[i
], "name"))
4960 search
->name
= argv
[++i
];
4961 else if(!irccasecmp(argv
[i
], "registrar"))
4962 search
->registrar
= argv
[++i
];
4963 else if(!irccasecmp(argv
[i
], "unvisited"))
4964 search
->unvisited
= ParseInterval(argv
[++i
]);
4965 else if(!irccasecmp(argv
[i
], "registered"))
4966 search
->registered
= ParseInterval(argv
[++i
]);
4967 else if(!irccasecmp(argv
[i
], "flags"))
4970 if(!irccasecmp(argv
[i
], "nodelete"))
4971 search
->flags
|= CHANNEL_NODELETE
;
4972 else if(!irccasecmp(argv
[i
], "suspended"))
4973 search
->flags
|= CHANNEL_SUSPENDED
;
4976 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4980 else if(!irccasecmp(argv
[i
], "limit"))
4981 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4984 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4989 if(search
->name
&& !strcmp(search
->name
, "*"))
4991 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4992 search
->registrar
= 0;
5001 chanserv_channel_match(struct chanData
*channel
, search_t search
)
5003 const char *name
= channel
->channel
->name
;
5004 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
5005 (search
->registrar
&& !channel
->registrar
) ||
5006 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
5007 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
5008 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
5009 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
5016 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
5018 struct chanData
*channel
;
5019 unsigned int matches
= 0;
5021 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
5023 if(!chanserv_channel_match(channel
, search
))
5033 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
5038 search_print(struct chanData
*channel
, void *data
)
5040 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
5043 static CHANSERV_FUNC(cmd_search
)
5046 unsigned int matches
;
5047 channel_search_func action
;
5051 if(!irccasecmp(argv
[1], "count"))
5052 action
= search_count
;
5053 else if(!irccasecmp(argv
[1], "print"))
5054 action
= search_print
;
5057 reply("CSMSG_ACTION_INVALID", argv
[1]);
5061 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
5065 if(action
== search_count
)
5066 search
->limit
= INT_MAX
;
5068 if(action
== search_print
)
5070 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5074 matches
= chanserv_channel_search(search
, action
, user
);
5077 reply("MSG_MATCH_COUNT", matches
);
5079 reply("MSG_NO_MATCHES");
5085 static CHANSERV_FUNC(cmd_unvisited
)
5087 struct chanData
*cData
;
5088 time_t interval
= chanserv_conf
.channel_expire_delay
;
5089 char buffer
[INTERVALLEN
];
5090 unsigned int limit
= 25, matches
= 0;
5094 interval
= ParseInterval(argv
[1]);
5096 limit
= atoi(argv
[2]);
5099 intervalString(buffer
, interval
, user
->handle_info
);
5100 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
5102 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
5104 if((now
- cData
->visited
) < interval
)
5107 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
5108 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
5115 static MODCMD_FUNC(chan_opt_defaulttopic
)
5121 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5123 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5127 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
5129 free(channel
->channel_info
->topic
);
5130 if(topic
[0] == '*' && topic
[1] == 0)
5132 topic
= channel
->channel_info
->topic
= NULL
;
5136 topic
= channel
->channel_info
->topic
= strdup(topic
);
5137 if(channel
->channel_info
->topic_mask
5138 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
5139 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5141 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
5144 if(channel
->channel_info
->topic
)
5145 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
5147 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
5151 static MODCMD_FUNC(chan_opt_topicmask
)
5155 struct chanData
*cData
= channel
->channel_info
;
5158 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
5160 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
5164 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
5166 if(cData
->topic_mask
)
5167 free(cData
->topic_mask
);
5168 if(mask
[0] == '*' && mask
[1] == 0)
5170 cData
->topic_mask
= 0;
5174 cData
->topic_mask
= strdup(mask
);
5176 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
5177 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
5178 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
5182 if(channel
->channel_info
->topic_mask
)
5183 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
5185 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
5189 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
5193 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
5197 if(greeting
[0] == '*' && greeting
[1] == 0)
5201 unsigned int length
= strlen(greeting
);
5202 if(length
> chanserv_conf
.greeting_length
)
5204 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
5207 *data
= strdup(greeting
);
5216 reply(name
, user_find_message(user
, "MSG_NONE"));
5220 static MODCMD_FUNC(chan_opt_greeting
)
5222 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
5225 static MODCMD_FUNC(chan_opt_usergreeting
)
5227 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
5230 static MODCMD_FUNC(chan_opt_modes
)
5232 struct mod_chanmode
*new_modes
;
5233 char modes
[MODELEN
];
5237 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
5239 reply("CSMSG_NO_ACCESS");
5242 if(argv
[1][0] == '*' && argv
[1][1] == 0)
5244 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
5246 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
|MCP_REGISTERED
)))
5248 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5251 else if(new_modes
->argc
> 1)
5253 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
5254 mod_chanmode_free(new_modes
);
5259 channel
->channel_info
->modes
= *new_modes
;
5260 modcmd_chanmode_announce(new_modes
);
5261 mod_chanmode_free(new_modes
);
5265 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
5267 reply("CSMSG_SET_MODES", modes
);
5269 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
5273 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5275 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5277 struct chanData
*cData
= channel
->channel_info
;
5282 /* Set flag according to value. */
5283 if(enabled_string(argv
[1]))
5285 cData
->flags
|= mask
;
5288 else if(disabled_string(argv
[1]))
5290 cData
->flags
&= ~mask
;
5295 reply("MSG_INVALID_BINARY", argv
[1]);
5301 /* Find current option value. */
5302 value
= (cData
->flags
& mask
) ? 1 : 0;
5306 reply(name
, user_find_message(user
, "MSG_ON"));
5308 reply(name
, user_find_message(user
, "MSG_OFF"));
5312 static MODCMD_FUNC(chan_opt_nodelete
)
5314 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
5316 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
5320 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
5323 static MODCMD_FUNC(chan_opt_dynlimit
)
5325 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
5328 static MODCMD_FUNC(chan_opt_offchannel
)
5330 struct chanData
*cData
= channel
->channel_info
;
5335 /* Set flag according to value. */
5336 if(enabled_string(argv
[1]))
5338 if(!IsOffChannel(cData
))
5339 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
5340 cData
->flags
|= CHANNEL_OFFCHANNEL
;
5343 else if(disabled_string(argv
[1]))
5345 if(IsOffChannel(cData
))
5347 struct mod_chanmode change
;
5348 mod_chanmode_init(&change
);
5350 change
.args
[0].mode
= MODE_CHANOP
;
5351 change
.args
[0].u
.member
= AddChannelUser(chanserv
, channel
);
5352 mod_chanmode_announce(chanserv
, channel
, &change
);
5354 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
5359 reply("MSG_INVALID_BINARY", argv
[1]);
5365 /* Find current option value. */
5366 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
5370 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
5372 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
5376 static MODCMD_FUNC(chan_opt_defaults
)
5378 struct userData
*uData
;
5379 struct chanData
*cData
;
5380 const char *confirm
;
5381 enum levelOption lvlOpt
;
5382 enum charOption chOpt
;
5384 cData
= channel
->channel_info
;
5385 uData
= GetChannelUser(cData
, user
->handle_info
);
5386 if(!uData
|| (uData
->access
< UL_OWNER
))
5388 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5391 confirm
= make_confirmation_string(uData
);
5392 if((argc
< 2) || strcmp(argv
[1], confirm
))
5394 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5397 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5398 cData
->modes
= chanserv_conf
.default_modes
;
5399 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5400 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5401 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5402 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5403 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5408 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5410 struct chanData
*cData
= channel
->channel_info
;
5411 struct userData
*uData
;
5412 unsigned short value
;
5416 if(!check_user_level(channel
, user
, option
, 1, 1))
5418 reply("CSMSG_CANNOT_SET");
5421 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5422 if(!value
&& strcmp(argv
[1], "0"))
5424 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5427 uData
= GetChannelUser(cData
, user
->handle_info
);
5428 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5430 reply("CSMSG_BAD_SETLEVEL");
5435 /* removing these level sets..
5437 if(value > cData->lvlOpts[lvlGiveOps])
5439 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5443 case lvlGiveHalfOps:
5444 if(value < cData->lvlOpts[lvlGiveVoice])
5446 reply("CSMSG_BAD_GIVEHOPS", cData->lvlOpts[lvlGiveHalfOps]);
5451 if(value < cData->lvlOpts[lvlGiveVoice])
5453 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5459 /* This test only applies to owners, since non-owners
5460 * trying to set an option to above their level get caught
5461 * by the CSMSG_BAD_SETLEVEL test above.
5463 if(value
> uData
->access
)
5465 reply("CSMSG_BAD_SETTERS");
5472 cData
->lvlOpts
[option
] = value
;
5474 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5478 static MODCMD_FUNC(chan_opt_enfops
)
5480 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5483 static MODCMD_FUNC(chan_opt_enfhalfops
)
5485 return channel_level_option(lvlEnfHalfOps
, CSFUNC_ARGS
);
5488 static MODCMD_FUNC(chan_opt_giveops)
5490 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5493 static MODCMD_FUNC(chan_opt_givehalfops)
5495 return channel_level_option(lvlGiveHalfOps, CSFUNC_ARGS);
5498 static MODCMD_FUNC(chan_opt_enfmodes
)
5500 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5503 static MODCMD_FUNC(chan_opt_enftopic
)
5505 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5508 static MODCMD_FUNC(chan_opt_pubcmd
)
5510 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5513 static MODCMD_FUNC(chan_opt_setters
)
5515 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5518 static MODCMD_FUNC(chan_opt_ctcpusers
)
5520 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5523 static MODCMD_FUNC(chan_opt_userinfo
)
5525 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5529 static MODCMD_FUNC(chan_opt_givevoice)
5531 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5535 static MODCMD_FUNC(chan_opt_topicsnarf
)
5537 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5540 static MODCMD_FUNC(chan_opt_inviteme
)
5542 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5546 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5548 struct chanData
*cData
= channel
->channel_info
;
5549 int count
= charOptions
[option
].count
, index
;
5553 index
= atoi(argv
[1]);
5555 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5557 reply("CSMSG_INVALID_NUMERIC", index
);
5558 /* Show possible values. */
5559 for(index
= 0; index
< count
; index
++)
5560 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5564 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5568 /* Find current option value. */
5571 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5575 /* Somehow, the option value is corrupt; reset it to the default. */
5576 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5581 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5585 static MODCMD_FUNC(chan_opt_voice
)
5587 return channel_multiple_option(chVoice
, CSFUNC_ARGS
);
5590 static MODCMD_FUNC(chan_opt_protect
)
5592 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5595 static MODCMD_FUNC(chan_opt_toys
)
5597 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5600 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5602 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5605 static MODCMD_FUNC(chan_opt_topicrefresh
)
5607 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5610 static struct svccmd_list set_shows_list
;
5613 handle_svccmd_unbind(struct svccmd
*target
) {
5615 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5616 if(target
== set_shows_list
.list
[ii
])
5617 set_shows_list
.used
= 0;
5620 static CHANSERV_FUNC(cmd_set
)
5622 struct svccmd
*subcmd
;
5626 /* Check if we need to (re-)initialize set_shows_list. */
5627 if(!set_shows_list
.used
)
5629 if(!set_shows_list
.size
)
5631 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5632 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5634 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5636 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5637 sprintf(buf
, "%s %s", argv
[0], name
);
5638 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5641 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5644 svccmd_list_append(&set_shows_list
, subcmd
);
5650 reply("CSMSG_CHANNEL_OPTIONS", channel
->name
);
5652 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5654 subcmd
= set_shows_list
.list
[ii
];
5655 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5657 reply("CSMSG_CHANNEL_OPTIONS_END");
5661 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5662 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5665 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5668 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5670 reply("CSMSG_NO_ACCESS");
5674 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5678 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5680 struct userData
*uData
;
5682 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5685 reply("CSMSG_NOT_USER", channel
->name
);
5691 /* Just show current option value. */
5693 else if(enabled_string(argv
[1]))
5695 uData
->flags
|= mask
;
5697 else if(disabled_string(argv
[1]))
5699 uData
->flags
&= ~mask
;
5703 reply("MSG_INVALID_BINARY", argv
[1]);
5707 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5711 static MODCMD_FUNC(user_opt_autoop
)
5713 struct userData
*uData
;
5715 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5718 reply("CSMSG_NOT_USER", channel
->name
);
5721 if(uData
->access
< UL_OP
/*channel->channel_info->lvlOpts[lvlGiveOps]*/)
5722 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5724 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5725 /* TODO: add halfops error message? or is the op one generic enough? */
5728 static MODCMD_FUNC(user_opt_autoinvite
)
5730 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5733 static MODCMD_FUNC(user_opt_info
)
5735 struct userData
*uData
;
5738 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5742 /* If they got past the command restrictions (which require access)
5743 * but fail this test, we have some fool with security override on.
5745 reply("CSMSG_NOT_USER", channel
->name
);
5752 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5753 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5755 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5758 bp
= strcspn(infoline
, "\001");
5761 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5766 if(infoline
[0] == '*' && infoline
[1] == 0)
5769 uData
->info
= strdup(infoline
);
5772 reply("CSMSG_USET_INFO", uData
->info
);
5774 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5778 struct svccmd_list uset_shows_list
;
5780 static CHANSERV_FUNC(cmd_uset
)
5782 struct svccmd
*subcmd
;
5786 /* Check if we need to (re-)initialize uset_shows_list. */
5787 if(!uset_shows_list
.used
)
5791 "AutoOp", "AutoInvite", "Info"
5794 if(!uset_shows_list
.size
)
5796 uset_shows_list
.size
= ArrayLength(options
);
5797 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5799 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5801 const char *name
= options
[ii
];
5802 sprintf(buf
, "%s %s", argv
[0], name
);
5803 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5806 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5809 svccmd_list_append(&uset_shows_list
, subcmd
);
5815 /* Do this so options are presented in a consistent order. */
5816 reply("CSMSG_USER_OPTIONS");
5817 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5818 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5822 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5823 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5826 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5830 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5833 static CHANSERV_FUNC(cmd_giveownership
)
5835 struct handle_info
*new_owner_hi
;
5836 struct userData
*new_owner
, *curr_user
;
5837 struct chanData
*cData
= channel
->channel_info
;
5838 struct do_not_register
*dnr
;
5840 unsigned short co_access
;
5841 char reason
[MAXLEN
];
5844 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5845 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5846 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5848 struct userData
*owner
= NULL
;
5849 for(curr_user
= channel
->channel_info
->users
;
5851 curr_user
= curr_user
->next
)
5853 if(curr_user
->access
!= UL_OWNER
)
5857 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5864 else if (!force
&& (now
< (time_t)(cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
)))
5866 char delay
[INTERVALLEN
];
5867 intervalString(delay
, cData
->ownerTransfer
+ chanserv_conf
.giveownership_period
- now
, user
->handle_info
);
5868 reply("CSMSG_TRANSFER_WAIT", delay
, channel
->name
);
5871 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5873 if(new_owner_hi
== user
->handle_info
)
5875 reply("CSMSG_NO_TRANSFER_SELF");
5878 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5883 new_owner
= add_channel_user(cData
, new_owner_hi
, UL_COOWNER
, 0, NULL
);
5887 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5891 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5893 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5896 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5897 if(!IsHelping(user
))
5898 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5900 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
->handle
);
5903 if(new_owner
->access
>= UL_COOWNER
)
5904 co_access
= new_owner
->access
;
5906 co_access
= UL_COOWNER
;
5907 new_owner
->access
= UL_OWNER
;
5909 curr_user
->access
= co_access
;
5910 cData
->ownerTransfer
= now
;
5911 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5912 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5913 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5917 static CHANSERV_FUNC(cmd_suspend
)
5919 struct handle_info
*hi
;
5920 struct userData
*self
, *target
;
5923 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5924 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5925 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5927 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5930 if(target
->access
>= self
->access
)
5932 reply("MSG_USER_OUTRANKED", hi
->handle
);
5935 if(target
->flags
& USER_SUSPENDED
)
5937 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5942 target
->present
= 0;
5945 target
->flags
|= USER_SUSPENDED
;
5946 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5950 static CHANSERV_FUNC(cmd_unsuspend
)
5952 struct handle_info
*hi
;
5953 struct userData
*self
, *target
;
5956 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5957 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5958 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5960 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5963 if(target
->access
>= self
->access
)
5965 reply("MSG_USER_OUTRANKED", hi
->handle
);
5968 if(!(target
->flags
& USER_SUSPENDED
))
5970 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5973 target
->flags
&= ~USER_SUSPENDED
;
5974 scan_user_presence(target
, NULL
);
5975 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5979 static MODCMD_FUNC(cmd_deleteme
)
5981 struct handle_info
*hi
;
5982 struct userData
*target
;
5983 const char *confirm_string
;
5984 unsigned short access
;
5987 hi
= user
->handle_info
;
5988 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5990 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5993 if(target
->access
== UL_OWNER
)
5995 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5998 confirm_string
= make_confirmation_string(target
);
5999 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
6001 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
6004 access
= target
->access
;
6005 channel_name
= strdup(channel
->name
);
6006 del_channel_user(target
, 1);
6007 reply("CSMSG_DELETED_YOU", access
, channel_name
);
6013 chanserv_refresh_topics(UNUSED_ARG(void *data
))
6015 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
6016 struct chanData
*cData
;
6019 for(cData
= channelList
; cData
; cData
= cData
->next
)
6021 if(IsSuspended(cData
))
6023 opt
= cData
->chOpts
[chTopicRefresh
];
6026 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
6029 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
6030 cData
->last_refresh
= refresh_num
;
6032 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
6035 static CHANSERV_FUNC(cmd_unf
)
6039 char response
[MAXLEN
];
6040 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
6041 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6042 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6045 reply("CSMSG_UNF_RESPONSE");
6049 static CHANSERV_FUNC(cmd_ping
)
6053 char response
[MAXLEN
];
6054 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
6055 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6056 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6059 reply("CSMSG_PING_RESPONSE");
6063 static CHANSERV_FUNC(cmd_wut
)
6067 char response
[MAXLEN
];
6068 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
6069 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
6070 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6073 reply("CSMSG_WUT_RESPONSE");
6077 static CHANSERV_FUNC(cmd_8ball
)
6079 unsigned int i
, j
, accum
;
6084 for(i
=1; i
<argc
; i
++)
6085 for(j
=0; argv
[i
][j
]; j
++)
6086 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
6087 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
6090 char response
[MAXLEN
];
6091 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
6092 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
6095 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
6099 static CHANSERV_FUNC(cmd_d
)
6101 unsigned long sides
, count
, modifier
, ii
, total
;
6102 char response
[MAXLEN
], *sep
;
6106 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
6116 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
6117 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
6121 else if((sep
[0] == '-') && isdigit(sep
[1]))
6122 modifier
= strtoul(sep
, NULL
, 10);
6123 else if((sep
[0] == '+') && isdigit(sep
[1]))
6124 modifier
= strtoul(sep
+1, NULL
, 10);
6131 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
6136 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
6139 for(total
= ii
= 0; ii
< count
; ++ii
)
6140 total
+= (rand() % sides
) + 1;
6143 if((count
> 1) || modifier
)
6145 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
6146 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
6150 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
6151 sprintf(response
, fmt
, total
, sides
);
6154 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6156 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6160 static CHANSERV_FUNC(cmd_huggle
)
6162 /* CTCP must be via PRIVMSG, never notice */
6164 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
6166 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
6170 static CHANSERV_FUNC(cmd_calc
)
6172 char response
[MAXLEN
];
6175 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
6178 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
6180 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
6185 chanserv_adjust_limit(void *data
)
6187 struct mod_chanmode change
;
6188 struct chanData
*cData
= data
;
6189 struct chanNode
*channel
= cData
->channel
;
6192 if(IsSuspended(cData
))
6195 cData
->limitAdjusted
= now
;
6196 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
6197 if(cData
->modes
.modes_set
& MODE_LIMIT
)
6199 if(limit
> cData
->modes
.new_limit
)
6200 limit
= cData
->modes
.new_limit
;
6201 else if(limit
== cData
->modes
.new_limit
)
6205 mod_chanmode_init(&change
);
6206 change
.modes_set
= MODE_LIMIT
;
6207 change
.new_limit
= limit
;
6208 mod_chanmode_announce(chanserv
, channel
, &change
);
6212 handle_new_channel(struct chanNode
*channel
)
6214 struct chanData
*cData
;
6216 if(!(cData
= channel
->channel_info
))
6219 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
6220 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
6222 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
6223 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
6226 /* Welcome to my worst nightmare. Warning: Read (or modify)
6227 the code below at your own risk. */
6229 handle_join(struct modeNode
*mNode
)
6231 struct mod_chanmode change
;
6232 struct userNode
*user
= mNode
->user
;
6233 struct chanNode
*channel
= mNode
->channel
;
6234 struct chanData
*cData
;
6235 struct userData
*uData
= NULL
;
6236 struct banData
*bData
;
6237 struct handle_info
*handle
;
6238 unsigned int modes
= 0, info
= 0;
6241 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6244 cData
= channel
->channel_info
;
6245 if(channel
->members
.used
> cData
->max
)
6246 cData
->max
= channel
->members
.used
;
6248 /* Check for bans. If they're joining through a ban, one of two
6250 * 1: Join during a netburst, by riding the break. Kick them
6251 * unless they have ops or voice in the channel.
6252 * 2: They're allowed to join through the ban (an invite in
6253 * ircu2.10, or a +e on Hybrid, or something).
6254 * If they're not joining through a ban, and the banlist is not
6255 * full, see if they're on the banlist for the channel. If so,
6258 /* This is really, really stupid. not all banned people are kicked.
6259 * sometimes we like to leave them unkicked.
6260 * I tried to explain this to the srvx developers and
6261 * got insulted.. hence one reason for this fork.
6263 if(user->uplink->burst && !mNode->modes)
6266 for(ii = 0; ii < channel->banlist.used; ii++)
6268 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
6270 ** Riding a netburst. Naughty. **
6271 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6278 mod_chanmode_init(&change
);
6280 if(channel
->banlist
.used
< MAXBANS
)
6282 /* Not joining through a ban. */
6283 for(bData
= cData
->bans
;
6284 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
6285 bData
= bData
->next
);
6289 char kick_reason
[MAXLEN
];
6290 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6292 bData
->triggered
= now
;
6293 if(bData
!= cData
->bans
)
6295 /* Shuffle the ban to the head of the list. */
6297 bData
->next
->prev
= bData
->prev
;
6299 bData
->prev
->next
= bData
->next
;
6302 bData
->next
= cData
->bans
;
6305 cData
->bans
->prev
= bData
;
6306 cData
->bans
= bData
;
6309 change
.args
[0].mode
= MODE_BAN
;
6310 change
.args
[0].u
.hostmask
= bData
->mask
;
6311 mod_chanmode_announce(chanserv
, channel
, &change
);
6312 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6317 /* ChanServ will not modify the limits in join-flooded channels.
6318 It will also skip DynLimit processing when the user (or srvx)
6319 is bursting in, because there are likely more incoming. */
6320 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6321 && !user
->uplink
->burst
6322 && !channel
->join_flooded
6323 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
6325 /* The user count has begun "bumping" into the channel limit,
6326 so set a timer to raise the limit a bit. Any previous
6327 timers are removed so three incoming users within the delay
6328 results in one limit change, not three. */
6330 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6331 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6334 if(channel
->join_flooded
)
6336 /* don't automatically give non users ops or voice during a join flood */
6338 /* EVERYONE is to get voice */
6339 else if(cData
->chOpts
[chVoice
] == 'a')
6340 modes
|= MODE_VOICE
;
6342 greeting
= cData
->greeting
;
6343 if(user
->handle_info
)
6345 handle
= user
->handle_info
;
6347 if(IsHelper(user
) && !IsHelping(user
))
6350 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6352 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
6354 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6360 uData
= GetTrueChannelAccess(cData
, handle
);
6361 if(uData
&& !IsUserSuspended(uData
))
6363 /* non users getting voice are handled above. */
6364 if(IsUserAutoOp(uData
))
6366 if(uData
->access
>= UL_OP
/*cData->lvlOpts[lvlGiveOps]*/)
6367 modes
|= MODE_CHANOP
;
6368 if(uData
->access
>= UL_HALFOP
/*cData->lvlOpts[lvlGiveHalfOps]*/)
6369 modes
|= MODE_HALFOP
;
6370 else if(uData
->access
>= UL_PEON
&& cData
->chOpts
[chVoice
] == 'p')
6371 modes
|= MODE_VOICE
;
6373 if(uData
->access
>= UL_PRESENT
)
6374 cData
->visited
= now
;
6375 if(cData
->user_greeting
)
6376 greeting
= cData
->user_greeting
;
6378 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
6379 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
6386 if(!user
->uplink
->burst
)
6390 if(modes
& MODE_CHANOP
) {
6391 modes
&= ~MODE_HALFOP
;
6392 modes
&= ~MODE_VOICE
;
6394 change
.args
[0].mode
= modes
;
6395 change
.args
[0].u
.member
= mNode
;
6396 mod_chanmode_announce(chanserv
, channel
, &change
);
6398 if(greeting
&& !user
->uplink
->burst
)
6399 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
6401 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
6407 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
6409 struct mod_chanmode change
;
6410 struct userData
*channel
;
6411 unsigned int ii
, jj
;
6413 if(!user
->handle_info
)
6416 mod_chanmode_init(&change
);
6418 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
6420 struct chanNode
*cn
;
6421 struct modeNode
*mn
;
6422 if(IsUserSuspended(channel
)
6423 || IsSuspended(channel
->channel
)
6424 || !(cn
= channel
->channel
->channel
))
6427 mn
= GetUserMode(cn
, user
);
6430 if(!IsUserSuspended(channel
)
6431 && IsUserAutoInvite(channel
)
6432 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
6434 && !user
->uplink
->burst
)
6435 irc_invite(chanserv
, user
, cn
);
6439 if(channel
->access
>= UL_PRESENT
)
6440 channel
->channel
->visited
= now
;
6442 if(IsUserAutoOp(channel
))
6444 if(channel
->access
>= UL_OP
/* cn->channel_info->lvlOpts[lvlGiveOps] */)
6445 change
.args
[0].mode
= MODE_CHANOP
;
6446 else if(channel
->access
>= UL_HALFOP
/* cn->channel_info->lvlOpts[lvlGiveHalfOps]*/)
6447 change
.args
[0].mode
= MODE_HALFOP
;
6448 else if(channel
->access
>= UL_PEON
/* cn->channel_info->lvlOpts[lvlGiveVoice]*/)
6449 change
.args
[0].mode
= MODE_VOICE
;
6451 change
.args
[0].mode
= 0;
6452 change
.args
[0].u
.member
= mn
;
6453 if(change
.args
[0].mode
)
6454 mod_chanmode_announce(chanserv
, cn
, &change
);
6457 channel
->seen
= now
;
6458 channel
->present
= 1;
6461 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6463 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6464 struct banData
*ban
;
6466 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6467 || !channel
->channel_info
6468 || IsSuspended(channel
->channel_info
))
6470 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6471 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6473 if(jj
< channel
->banlist
.used
)
6475 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6477 char kick_reason
[MAXLEN
];
6478 if(!user_matches_glob(user
, ban
->mask
, 1))
6480 change
.args
[0].mode
= MODE_BAN
;
6481 change
.args
[0].u
.hostmask
= ban
->mask
;
6482 mod_chanmode_announce(chanserv
, channel
, &change
);
6483 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6484 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6485 ban
->triggered
= now
;
6490 if(IsSupportHelper(user
))
6492 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6494 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6496 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6504 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6506 struct chanData
*cData
;
6507 struct userData
*uData
;
6509 cData
= mn
->channel
->channel_info
;
6510 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6513 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6515 /* Allow for a bit of padding so that the limit doesn't
6516 track the user count exactly, which could get annoying. */
6517 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6519 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6520 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6524 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6526 scan_user_presence(uData
, mn
->user
);
6530 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6532 unsigned int ii
, jj
;
6533 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6535 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6536 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6538 if(jj
< mn
->user
->channels
.used
)
6541 if(ii
== chanserv_conf
.support_channels
.used
)
6542 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6547 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6549 struct userData
*uData
;
6551 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6552 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6553 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6556 if(protect_user(victim
, kicker
, channel
->channel_info
))
6558 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6559 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6562 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6567 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6569 struct chanData
*cData
;
6571 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6574 cData
= channel
->channel_info
;
6575 if(bad_topic(channel
, user
, channel
->topic
))
6576 { /* User doesnt have privs to set topics. Undo it */
6577 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6578 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6581 /* If there is a topic mask set, and the new topic doesnt match,
6582 * set the topic to mask + new_topic */
6583 if(cData
->topic_mask
&& !match_ircglob(channel
->topic
, cData
->topic_mask
))
6585 char new_topic
[TOPICLEN
+1];
6586 conform_topic(cData
->topic_mask
, channel
->topic
, new_topic
);
6589 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
6590 /* and fall through to topicsnarf code below.. */
6592 else /* Topic couldnt fit into mask, was too long */
6594 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6595 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT1", channel
->name
, cData
->topic_mask
);
6596 send_message(user
, chanserv
, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
6600 /* With topicsnarf, grab the topic and save it as the default topic. */
6601 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6604 cData
->topic
= strdup(channel
->topic
);
6610 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6612 struct mod_chanmode
*bounce
= NULL
;
6613 unsigned int bnc
, ii
;
6616 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6619 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6620 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6622 char correct
[MAXLEN
];
6623 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6624 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6625 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6627 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6629 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6631 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6632 if(!protect_user(victim
, user
, channel
->channel_info
))
6635 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6638 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6639 bounce
->args
[bnc
].u
.member
= GetUserMode(channel
, user
);
6640 if(bounce
->args
[bnc
].u
.member
)
6644 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6645 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6647 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6649 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6651 const struct userNode
*victim
= change
->args
[ii
].u
.member
->user
;
6652 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6655 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6656 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6657 bounce
->args
[bnc
].u
.member
= change
->args
[ii
].u
.member
;
6660 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6662 const char *ban
= change
->args
[ii
].u
.hostmask
;
6663 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6666 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6667 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6668 bounce
->args
[bnc
].u
.hostmask
= strdup(ban
);
6670 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6675 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6676 mod_chanmode_announce(chanserv
, channel
, bounce
);
6677 for(ii
= 0; ii
< change
->argc
; ++ii
)
6678 if(bounce
->args
[ii
].mode
== (MODE_REMOVE
| MODE_BAN
))
6679 free((char*)bounce
->args
[ii
].u
.hostmask
);
6680 mod_chanmode_free(bounce
);
6685 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6687 struct chanNode
*channel
;
6688 struct banData
*bData
;
6689 struct mod_chanmode change
;
6690 unsigned int ii
, jj
;
6691 char kick_reason
[MAXLEN
];
6693 mod_chanmode_init(&change
);
6695 change
.args
[0].mode
= MODE_BAN
;
6696 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6698 channel
= user
->channels
.list
[ii
]->channel
;
6699 /* Need not check for bans if they're opped or voiced. */
6700 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_HALFOP
|MODE_VOICE
))
6702 /* Need not check for bans unless channel registration is active. */
6703 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6705 /* Look for a matching ban already on the channel. */
6706 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6707 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6709 /* Need not act if we found one. */
6710 if(jj
< channel
->banlist
.used
)
6712 /* Look for a matching ban in this channel. */
6713 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6715 if(!user_matches_glob(user
, bData
->mask
, 1))
6717 change
.args
[0].u
.hostmask
= bData
->mask
;
6718 mod_chanmode_announce(chanserv
, channel
, &change
);
6719 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6720 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6721 bData
->triggered
= now
;
6722 break; /* we don't need to check any more bans in the channel */
6727 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6729 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6733 dict_remove2(handle_dnrs
, old_handle
, 1);
6734 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6735 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6740 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6742 struct userNode
*h_user
;
6744 if(handle
->channels
)
6746 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6747 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6749 while(handle
->channels
)
6750 del_channel_user(handle
->channels
, 1);
6755 handle_server_link(UNUSED_ARG(struct server
*server
))
6757 struct chanData
*cData
;
6759 for(cData
= channelList
; cData
; cData
= cData
->next
)
6761 if(!IsSuspended(cData
))
6762 cData
->may_opchan
= 1;
6763 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6764 && !cData
->channel
->join_flooded
6765 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6766 < chanserv_conf
.adjust_threshold
))
6768 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6769 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6775 chanserv_conf_read(void)
6779 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6780 struct mod_chanmode
*change
;
6781 struct string_list
*strlist
;
6782 struct chanNode
*chan
;
6785 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6787 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6790 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6791 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6792 chanserv_conf
.support_channels
.used
= 0;
6793 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6795 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6797 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6800 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
, NULL
);
6802 channelList_append(&chanserv_conf
.support_channels
, chan
);
6805 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6808 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6811 chan
= AddChannel(str
, now
, str2
, NULL
, NULL
);
6813 channelList_append(&chanserv_conf
.support_channels
, chan
);
6815 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6816 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6817 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6818 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6819 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6820 chanserv_conf
.greeting_length
= str
? atoi(str
) : 200;
6821 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6822 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6823 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6824 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6825 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6826 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6827 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6828 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6829 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6830 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6831 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6832 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6833 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6834 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6835 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6836 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6837 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6839 NickChange(chanserv
, str
, 0);
6840 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6841 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6842 str
= database_get_data(conf_node
, KEY_GIVEOWNERSHIP_PERIOD
, RECDB_QSTRING
);
6843 chanserv_conf
.giveownership_period
= str
? ParseInterval(str
) : 0;
6844 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6845 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6846 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6847 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6848 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6849 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6850 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6851 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6852 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6853 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6854 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6855 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6856 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6859 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6860 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6861 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6863 chanserv_conf
.default_modes
= *change
;
6864 mod_chanmode_free(change
);
6866 free_string_list(chanserv_conf
.set_shows
);
6867 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6869 strlist
= string_list_copy(strlist
);
6872 static const char *list
[] = {
6873 /* free form text */
6874 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6875 /* options based on user level */
6876 "PubCmd", "InviteMe", "UserInfo",/* "GiveVoice", "GiveHalfOps", "GiveOps", */ "EnfOps",
6877 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6878 /* multiple choice options */
6879 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6880 /* binary options */
6881 "DynLimit", "NoDelete",
6886 strlist
= alloc_string_list(ArrayLength(list
)-1);
6887 for(ii
=0; list
[ii
]; ii
++)
6888 string_list_append(strlist
, strdup(list
[ii
]));
6890 chanserv_conf
.set_shows
= strlist
;
6891 /* We don't look things up now, in case the list refers to options
6892 * defined by modules initialized after this point. Just mark the
6893 * function list as invalid, so it will be initialized.
6895 set_shows_list
.used
= 0;
6896 free_string_list(chanserv_conf
.eightball
);
6897 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6900 strlist
= string_list_copy(strlist
);
6904 strlist
= alloc_string_list(4);
6905 string_list_append(strlist
, strdup("Yes."));
6906 string_list_append(strlist
, strdup("No."));
6907 string_list_append(strlist
, strdup("Maybe so."));
6909 chanserv_conf
.eightball
= strlist
;
6910 free_string_list(chanserv_conf
.old_ban_names
);
6911 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6913 strlist
= string_list_copy(strlist
);
6915 strlist
= alloc_string_list(2);
6916 chanserv_conf
.old_ban_names
= strlist
;
6917 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6918 off_channel
= str
? atoi(str
) : 0;
6922 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6925 struct note_type
*ntype
;
6928 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6930 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6933 if(!(ntype
= chanserv_create_note_type(key
)))
6935 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6939 /* Figure out set access */
6940 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6942 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6943 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6945 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6947 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6948 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6950 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6952 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6956 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6957 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6958 ntype
->set_access
.min_opserv
= 0;
6961 /* Figure out visibility */
6962 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6963 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6964 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6965 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6966 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6967 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6968 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6969 ntype
->visible_type
= NOTE_VIS_ALL
;
6971 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6973 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6974 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6978 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6980 struct handle_info
*handle
;
6981 struct userData
*uData
;
6982 char *seen
, *inf
, *flags
;
6984 unsigned short access
;
6986 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6988 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6992 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6993 if(access
> UL_OWNER
)
6995 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6999 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
7000 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
7001 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
7002 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
7003 handle
= get_handle_info(key
);
7006 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
7010 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
7011 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
7013 /* Upgrade: set autoop to the inverse of noautoop */
7014 if(chanserv_read_version
< 2)
7016 /* if noautoop is true, set autoop false, and vice versa */
7017 if(uData
->flags
& USER_NOAUTO_OP
)
7018 uData
->flags
= uData
->flags
& ~USER_AUTO_OP
;
7020 uData
->flags
= uData
->flags
| USER_AUTO_OP
;
7021 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
);
7027 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
7029 struct banData
*bData
;
7030 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
7031 time_t set_time
, triggered_time
, expires_time
;
7033 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
7035 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
7039 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
7040 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
7041 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
7042 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
7043 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
7044 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
7045 if (!reason
|| !owner
)
7048 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
7049 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
7051 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
7053 expires_time
= set_time
+ atoi(s_duration
);
7057 if(!reason
|| (expires_time
&& (expires_time
< now
)))
7060 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
7063 static struct suspended
*
7064 chanserv_read_suspended(dict_t obj
)
7066 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
7070 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
7071 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7072 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
7073 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7074 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
7075 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7076 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
7077 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
7078 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
7079 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
7084 chanserv_channel_read(const char *key
, struct record_data
*hir
)
7086 struct suspended
*suspended
;
7087 struct mod_chanmode
*modes
;
7088 struct chanNode
*cNode
;
7089 struct chanData
*cData
;
7090 struct dict
*channel
, *obj
;
7091 char *str
, *argv
[10];
7095 channel
= hir
->d
.object
;
7097 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
7100 cNode
= AddChannel(key
, now
, NULL
, NULL
, NULL
);
7103 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
7106 cData
= register_channel(cNode
, str
);
7109 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
7113 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
7115 enum levelOption lvlOpt
;
7116 enum charOption chOpt
;
7118 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
7119 cData
->flags
= atoi(str
);
7121 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7123 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
7125 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
7126 else if(levelOptions
[lvlOpt
].old_flag
)
7128 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7129 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
7131 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
7135 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7137 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
7139 cData
->chOpts
[chOpt
] = str
[0];
7142 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
7144 enum levelOption lvlOpt
;
7145 enum charOption chOpt
;
7148 cData
->flags
= base64toint(str
, 5);
7149 count
= strlen(str
+= 5);
7150 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7153 if(levelOptions
[lvlOpt
].old_flag
)
7155 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
7156 lvl
= levelOptions
[lvlOpt
].flag_value
;
7158 lvl
= levelOptions
[lvlOpt
].default_value
;
7160 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
7162 case 'c': lvl
= UL_COOWNER
; break;
7163 case 'm': lvl
= UL_MANAGER
; break;
7164 case 'n': lvl
= UL_OWNER
+1; break;
7165 case 'o': lvl
= UL_OP
; break;
7166 case 'p': lvl
= UL_PEON
; break;
7167 case 'h': lvl
= UL_HALFOP
; break;
7168 case 'w': lvl
= UL_OWNER
; break;
7169 default: lvl
= 0; break;
7171 cData
->lvlOpts
[lvlOpt
] = lvl
;
7173 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7174 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
7177 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
7179 suspended
= chanserv_read_suspended(obj
);
7180 cData
->suspended
= suspended
;
7181 suspended
->cData
= cData
;
7182 /* We could use suspended->expires and suspended->revoked to
7183 * set the CHANNEL_SUSPENDED flag, but we don't. */
7185 else if(IsSuspended(cData
) && (str
= database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
)))
7187 suspended
= calloc(1, sizeof(*suspended
));
7188 suspended
->issued
= 0;
7189 suspended
->revoked
= 0;
7190 suspended
->suspender
= strdup(str
);
7191 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
7192 suspended
->expires
= str
? atoi(str
) : 0;
7193 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
7194 suspended
->reason
= strdup(str
? str
: "No reason");
7195 suspended
->previous
= NULL
;
7196 cData
->suspended
= suspended
;
7197 suspended
->cData
= cData
;
7201 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7202 suspended
= NULL
; /* to squelch a warning */
7205 if(IsSuspended(cData
)) {
7206 if(suspended
->expires
> now
)
7207 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
7208 else if(suspended
->expires
)
7209 cData
->flags
&= ~CHANNEL_SUSPENDED
;
7212 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
7213 struct mod_chanmode change
;
7214 mod_chanmode_init(&change
);
7216 change
.args
[0].mode
= MODE_CHANOP
;
7217 change
.args
[0].u
.member
= AddChannelUser(chanserv
, cNode
);
7218 mod_chanmode_announce(chanserv
, cNode
, &change
);
7221 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
7222 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7223 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
7224 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
7225 str
= database_get_data(channel
, KEY_OWNER_TRANSFER
, RECDB_QSTRING
);
7226 cData
->ownerTransfer
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
7227 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
7228 cData
->max
= str
? atoi(str
) : 0;
7229 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
7230 cData
->greeting
= str
? strdup(str
) : NULL
;
7231 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
7232 cData
->user_greeting
= str
? strdup(str
) : NULL
;
7233 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
7234 cData
->topic_mask
= str
? strdup(str
) : NULL
;
7235 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
7236 cData
->topic
= str
? strdup(str
) : NULL
;
7238 if(!IsSuspended(cData
)
7239 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
7240 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
7241 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
7242 cData
->modes
= *modes
;
7244 cData
->modes
.modes_set
|= MODE_REGISTERED
;
7245 if(cData
->modes
.argc
> 1)
7246 cData
->modes
.argc
= 1;
7247 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
7248 mod_chanmode_free(modes
);
7251 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
7252 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7253 user_read_helper(iter_key(it
), iter_data(it
), cData
);
7255 if(!cData
->users
&& !IsProtected(cData
))
7257 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
7258 unregister_channel(cData
, "has empty user list.");
7262 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
7263 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7264 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
7266 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
7267 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
7269 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
7270 struct record_data
*rd
= iter_data(it
);
7271 const char *note
, *setter
;
7273 if(rd
->type
!= RECDB_OBJECT
)
7275 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
7279 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
7281 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
7283 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
7287 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
7288 if(!setter
) setter
= "<unknown>";
7289 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
7297 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
7299 const char *setter
, *reason
, *str
;
7300 struct do_not_register
*dnr
;
7302 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
7305 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
7308 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
7311 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
7314 dnr
= chanserv_add_dnr(key
, setter
, reason
);
7317 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
7319 dnr
->set
= atoi(str
);
7325 chanserv_version_read(struct dict
*section
)
7329 str
= database_get_data(section
, KEY_VERSION_NUMBER
, RECDB_QSTRING
);
7331 chanserv_read_version
= atoi(str
);
7332 log_module(CS_LOG
, LOG_DEBUG
, "Chanserv db version is %d.", chanserv_read_version
);
7336 chanserv_saxdb_read(struct dict
*database
)
7338 struct dict
*section
;
7341 if((section
= database_get_data(database
, KEY_VERSION_CONTROL
, RECDB_OBJECT
)))
7342 chanserv_version_read(section
);
7344 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
7345 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7346 chanserv_note_type_read(iter_key(it
), iter_data(it
));
7348 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
7349 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7350 chanserv_channel_read(iter_key(it
), iter_data(it
));
7352 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
7353 for(it
= dict_first(section
); it
; it
= iter_next(it
))
7354 chanserv_dnr_read(iter_key(it
), iter_data(it
));
7360 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
7362 int high_present
= 0;
7363 saxdb_start_record(ctx
, KEY_USERS
, 1);
7364 for(; uData
; uData
= uData
->next
)
7366 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
7368 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
7369 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
7370 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
7372 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
7374 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
7375 saxdb_end_record(ctx
);
7377 saxdb_end_record(ctx
);
7378 return high_present
;
7382 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
7386 saxdb_start_record(ctx
, KEY_BANS
, 1);
7387 for(; bData
; bData
= bData
->next
)
7389 saxdb_start_record(ctx
, bData
->mask
, 0);
7390 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
7391 if(bData
->triggered
)
7392 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
7394 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
7396 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
7398 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
7399 saxdb_end_record(ctx
);
7401 saxdb_end_record(ctx
);
7405 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
7407 saxdb_start_record(ctx
, name
, 0);
7408 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
7409 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
7411 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
7413 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
7415 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
7417 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
7418 saxdb_end_record(ctx
);
7422 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
7426 enum levelOption lvlOpt
;
7427 enum charOption chOpt
;
7429 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
7431 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
7432 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
7434 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
7435 if(channel
->registrar
)
7436 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
7437 if(channel
->greeting
)
7438 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
7439 if(channel
->user_greeting
)
7440 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
7441 if(channel
->topic_mask
)
7442 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
7443 if(channel
->suspended
)
7444 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
7446 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
7447 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
7448 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
7449 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
7450 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
7452 buf
[0] = channel
->chOpts
[chOpt
];
7454 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
7456 saxdb_end_record(ctx
);
7458 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
7460 mod_chanmode_format(&channel
->modes
, buf
);
7461 saxdb_write_string(ctx
, KEY_MODES
, buf
);
7464 high_present
= chanserv_write_users(ctx
, channel
->users
);
7465 chanserv_write_bans(ctx
, channel
->bans
);
7467 if(dict_size(channel
->notes
))
7471 saxdb_start_record(ctx
, KEY_NOTES
, 1);
7472 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
7474 struct note
*note
= iter_data(it
);
7475 saxdb_start_record(ctx
, iter_key(it
), 0);
7476 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
7477 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
7478 saxdb_end_record(ctx
);
7480 saxdb_end_record(ctx
);
7483 if(channel
->ownerTransfer
)
7484 saxdb_write_int(ctx
, KEY_OWNER_TRANSFER
, channel
->ownerTransfer
);
7485 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
7486 saxdb_end_record(ctx
);
7490 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
7494 saxdb_start_record(ctx
, ntype
->name
, 0);
7495 switch(ntype
->set_access_type
)
7497 case NOTE_SET_CHANNEL_ACCESS
:
7498 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7500 case NOTE_SET_CHANNEL_SETTER
:
7501 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7503 case NOTE_SET_PRIVILEGED
: default:
7504 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7507 switch(ntype
->visible_type
)
7509 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7510 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7511 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7513 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7514 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7515 saxdb_end_record(ctx
);
7519 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7521 struct do_not_register
*dnr
;
7524 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7526 dnr
= iter_data(it
);
7527 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7529 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7530 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7531 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7532 saxdb_end_record(ctx
);
7537 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7540 struct chanData
*channel
;
7542 /* Version Control*/
7543 saxdb_start_record(ctx
, KEY_VERSION_CONTROL
, 1);
7544 saxdb_write_int(ctx
, KEY_VERSION_NUMBER
, CHANSERV_DB_VERSION
);
7545 saxdb_end_record(ctx
);
7548 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7549 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7550 chanserv_write_note_type(ctx
, iter_data(it
));
7551 saxdb_end_record(ctx
);
7554 saxdb_start_record(ctx
, KEY_DNR
, 1);
7555 write_dnrs_helper(ctx
, handle_dnrs
);
7556 write_dnrs_helper(ctx
, plain_dnrs
);
7557 write_dnrs_helper(ctx
, mask_dnrs
);
7558 saxdb_end_record(ctx
);
7561 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7562 for(channel
= channelList
; channel
; channel
= channel
->next
)
7563 chanserv_write_channel(ctx
, channel
);
7564 saxdb_end_record(ctx
);
7570 chanserv_db_cleanup(void) {
7572 unreg_part_func(handle_part
);
7574 unregister_channel(channelList
, "terminating.");
7575 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7576 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7577 free(chanserv_conf
.support_channels
.list
);
7578 dict_delete(handle_dnrs
);
7579 dict_delete(plain_dnrs
);
7580 dict_delete(mask_dnrs
);
7581 dict_delete(note_types
);
7582 free_string_list(chanserv_conf
.eightball
);
7583 free_string_list(chanserv_conf
.old_ban_names
);
7584 free_string_list(chanserv_conf
.set_shows
);
7585 free(set_shows_list
.list
);
7586 free(uset_shows_list
.list
);
7589 struct userData
*helper
= helperList
;
7590 helperList
= helperList
->next
;
7595 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7596 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7597 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7600 init_chanserv(const char *nick
)
7602 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7603 conf_register_reload(chanserv_conf_read
);
7605 reg_server_link_func(handle_server_link
);
7607 reg_new_channel_func(handle_new_channel
);
7608 reg_join_func(handle_join
);
7609 reg_part_func(handle_part
);
7610 reg_kick_func(handle_kick
);
7611 reg_topic_func(handle_topic
);
7612 reg_mode_change_func(handle_mode
);
7613 reg_nick_change_func(handle_nick_change
);
7615 reg_auth_func(handle_auth
);
7616 reg_handle_rename_func(handle_rename
);
7617 reg_unreg_func(handle_unreg
);
7619 handle_dnrs
= dict_new();
7620 dict_set_free_data(handle_dnrs
, free
);
7621 plain_dnrs
= dict_new();
7622 dict_set_free_data(plain_dnrs
, free
);
7623 mask_dnrs
= dict_new();
7624 dict_set_free_data(mask_dnrs
, free
);
7628 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7629 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7630 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7631 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7632 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7633 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7634 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7635 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7636 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7637 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7639 DEFINE_COMMAND(pending
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7641 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7642 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7644 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7645 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7646 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7647 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7648 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7650 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7651 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7652 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7653 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7654 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7655 DEFINE_COMMAND(mdelhalfop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7657 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7658 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7659 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7660 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7662 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7663 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7664 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7665 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7666 DEFINE_COMMAND(hop
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7667 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7668 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7669 DEFINE_COMMAND(dehop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7670 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7671 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7673 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7674 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7675 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7676 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7677 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7678 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7679 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7680 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7681 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7682 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7683 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7684 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7685 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7686 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7688 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7689 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7690 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7691 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7692 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7694 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7695 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7697 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7698 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7699 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7700 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7701 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7702 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7703 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7704 DEFINE_COMMAND(hlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7705 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7706 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7707 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7708 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7710 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7711 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7713 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7714 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7715 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7716 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7718 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7719 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7720 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7721 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7722 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7724 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7725 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7726 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7727 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7728 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7729 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7730 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7732 /* Channel options */
7733 DEFINE_CHANNEL_OPTION(defaulttopic
);
7734 DEFINE_CHANNEL_OPTION(topicmask
);
7735 DEFINE_CHANNEL_OPTION(greeting
);
7736 DEFINE_CHANNEL_OPTION(usergreeting
);
7737 DEFINE_CHANNEL_OPTION(modes
);
7738 DEFINE_CHANNEL_OPTION(enfops
);
7739 DEFINE_CHANNEL_OPTION(enfhalfops
);
7740 /*DEFINE_CHANNEL_OPTION(giveops);
7741 DEFINE_CHANNEL_OPTION(givehalfops);
7743 DEFINE_CHANNEL_OPTION(voice
);
7744 DEFINE_CHANNEL_OPTION(protect
);
7745 DEFINE_CHANNEL_OPTION(enfmodes
);
7746 DEFINE_CHANNEL_OPTION(enftopic
);
7747 DEFINE_CHANNEL_OPTION(pubcmd
);
7748 /*DEFINE_CHANNEL_OPTION(givevoice);
7750 DEFINE_CHANNEL_OPTION(userinfo
);
7751 DEFINE_CHANNEL_OPTION(dynlimit
);
7752 DEFINE_CHANNEL_OPTION(topicsnarf
);
7753 DEFINE_CHANNEL_OPTION(nodelete
);
7754 DEFINE_CHANNEL_OPTION(toys
);
7755 DEFINE_CHANNEL_OPTION(setters
);
7756 DEFINE_CHANNEL_OPTION(topicrefresh
);
7757 DEFINE_CHANNEL_OPTION(ctcpusers
);
7758 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7759 DEFINE_CHANNEL_OPTION(inviteme
);
7761 DEFINE_CHANNEL_OPTION(offchannel
);
7762 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7764 /* Alias set topic to set defaulttopic for compatibility. */
7765 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7768 DEFINE_USER_OPTION(autoinvite
);
7769 DEFINE_USER_OPTION(info
);
7770 DEFINE_USER_OPTION(autoop
);
7772 /* Alias uset autovoice to uset autoop. */
7773 modcmd_register(chanserv_module
, "uset autovoice", user_opt_autoop
, 1, 0, NULL
);
7775 note_types
= dict_new();
7776 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7779 const char *modes
= conf_get_data("services/chanserv/modes", RECDB_QSTRING
);
7780 chanserv
= AddService(nick
, modes
? modes
: NULL
, "Channel Services", NULL
);
7781 service_register(chanserv
)->trigger
= '!';
7782 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7785 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7787 if(chanserv_conf
.channel_expire_frequency
)
7788 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7790 if(chanserv_conf
.refresh_period
)
7792 time_t next_refresh
;
7793 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7794 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7797 reg_exit_func(chanserv_db_cleanup
);
7798 message_register_table(msgtab
);