]> jfr.im git - irc/evilnet/x3.git/blame - src/chanserv.c
Author: Rubin
[irc/evilnet/x3.git] / src / chanserv.c
CommitLineData
d76ed9a9 1/* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of srvx.
5 *
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.
10 *
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.
15 *
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.
19 */
20
21#include "chanserv.h"
22#include "conf.h"
23#include "global.h"
24#include "modcmd.h"
25#include "opserv.h" /* for opserv_bad_channel() */
26#include "saxdb.h"
27#include "timeq.h"
28
29#define CHANSERV_CONF_NAME "services/chanserv"
30
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_MAX_SWITCH_LOAD "max_switch_load"
46#define KEY_SWITCH_TIMEOUT "switch_timeout"
47#define KEY_8BALL_RESPONSES "8ball"
48#define KEY_OLD_BAN_NAMES "old_ban_names"
49#define KEY_REFRESH_PERIOD "refresh_period"
50#define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51#define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52#define KEY_MAX_OWNED "max_owned"
53#define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54#define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55#define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56#define KEY_NODELETE_LEVEL "nodelete_level"
57#define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58
59/* ChanServ database */
60#define KEY_CHANNELS "channels"
61#define KEY_NOTE_TYPES "note_types"
62
63/* Note type parameters */
64#define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66#define KEY_NOTE_SETTER_ACCESS "setter_access"
67#define KEY_NOTE_VISIBILITY "visibility"
68#define KEY_NOTE_VIS_PRIVILEGED "privileged"
69#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70#define KEY_NOTE_VIS_ALL "all"
71#define KEY_NOTE_MAX_LENGTH "max_length"
72#define KEY_NOTE_SETTER "setter"
73#define KEY_NOTE_NOTE "note"
74
75/* Do-not-register channels */
76#define KEY_DNR "dnr"
77#define KEY_DNR_SET "set"
78#define KEY_DNR_SETTER "setter"
79#define KEY_DNR_REASON "reason"
80
81/* Channel data */
82#define KEY_REGISTERED "registered"
83#define KEY_REGISTRAR "registrar"
84#define KEY_SUSPENDED "suspended"
85#define KEY_PREVIOUS "previous"
86#define KEY_SUSPENDER "suspender"
87#define KEY_ISSUED "issued"
88#define KEY_REVOKED "revoked"
89#define KEY_SUSPEND_EXPIRES "suspend_expires"
90#define KEY_SUSPEND_REASON "suspend_reason"
91#define KEY_VISITED "visited"
92#define KEY_TOPIC "topic"
93#define KEY_GREETING "greeting"
94#define KEY_USER_GREETING "user_greeting"
95#define KEY_MODES "modes"
96#define KEY_FLAGS "flags"
97#define KEY_OPTIONS "options"
98#define KEY_USERS "users"
99#define KEY_BANS "bans"
100#define KEY_MAX "max"
101#define KEY_NOTES "notes"
102#define KEY_TOPIC_MASK "topic_mask"
103
104/* User data */
105#define KEY_LEVEL "level"
106#define KEY_INFO "info"
107#define KEY_SEEN "seen"
108
109/* Ban data */
110#define KEY_OWNER "owner"
111#define KEY_REASON "reason"
112#define KEY_SET "set"
113#define KEY_DURATION "duration"
114#define KEY_EXPIRES "expires"
115#define KEY_TRIGGERED "triggered"
116
117#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118#define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
119
120/* Administrative messages */
121static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
123
124/* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
131
132/* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
144
145/* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
150
151/* Channel moving */
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
154
155/* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
161
162/* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
164
165/* Error messages */
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "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." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
179
180/* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
184
185/* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "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." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
194
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
199 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
200
201/* Ban management */
202 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
203 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
204 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
205 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
206 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
207 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
208 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
209 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
210 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
211 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
212 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
213 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
214 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
215 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
216 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
217 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
218
219 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
220
221/* Channel management */
222 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
223 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
224 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
225
226 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
227 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
228 { "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" },
229 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
230 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
231 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
232 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
233
234 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
235 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
236 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
237 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
238 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
239 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
240 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
241 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
242 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
243 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
244 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
245 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
246 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
247 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
248 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
249 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
250 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
251 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
252 { "CSMSG_SET_MODES", "$bModes $b %s" },
253 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
254 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
255 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
256 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
257 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
258 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
259 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
260 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
261 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
262 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
263 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
264 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
265 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
266 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
267 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
268 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
269 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
270 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
271 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
272 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
273 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
274 { "CSMSG_USET_INFO", "$bInfo $b %s" },
275
276 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
277 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
278 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
279 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
280 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
281 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
282 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
283 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
284 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
286 { "CSMSG_PROTECT_NONE", "No users will be protected." },
287 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
288 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
289 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
290 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
291 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
292 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
293 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
294 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
295 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
299
300 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
301 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
302 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
303 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
304 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
305 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
306 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
307 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
308
309 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
310 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
311 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
312
313/* Channel userlist */
314 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
315 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
316 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
317 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
318
319/* Channel note list */
320 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
321 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
322 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
323 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
324 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
325 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
326 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
327 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
328 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
329 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
330 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
331 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
332 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
333 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
334 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
335
336/* Channel [un]suspension */
337 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
338 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
339 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
340 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
341 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
342 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
343 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
344
345/* Access information */
346 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
347 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
348 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
349 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
350 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
351 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
352 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
353 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
354 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
355 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
356 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
357
358/* Seen information */
359 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
360 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
361 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
362 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
363
364/* Names information */
365 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
366 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
367
368/* Channel information */
369 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
370 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
371 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
372 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
373 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
374 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
375 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
376 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
377 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
378 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
379 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
380 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
388 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
389 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
390
391 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
392 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
393 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
394 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
395 { "CSMSG_PEEK_OPS", "$bOps:$b" },
396 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
397
398/* Network information */
399 { "CSMSG_NETWORK_INFO", "Network Information:" },
400 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
401 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
402 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
403 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
404 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
405 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
406 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
407 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
408
409/* Staff list */
410 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
411 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
412 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
413
414/* Channel searches */
415 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
416 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
417 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
418 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
419
420/* Channel configuration */
421 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
422 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
423 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
424
425/* User settings */
426 { "CSMSG_USER_OPTIONS", "User Options:" },
427 { "CSMSG_USER_PROTECTED", "That user is protected." },
428
429/* Toys */
430 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
431 { "CSMSG_PING_RESPONSE", "Pong!" },
432 { "CSMSG_WUT_RESPONSE", "wut" },
433 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
434 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
435 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
436 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
437 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
438 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
439 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
440
441/* Other things */
442 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
443 { NULL, NULL }
444};
445
446/* eject_user and unban_user flags */
447#define ACTION_KICK 0x0001
448#define ACTION_BAN 0x0002
449#define ACTION_ADD_BAN 0x0004
450#define ACTION_ADD_TIMED_BAN 0x0008
451#define ACTION_UNBAN 0x0010
452#define ACTION_DEL_BAN 0x0020
453
454/* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
455#define MODELEN 40 + KEYLEN
456#define PADLEN 21
457#define ACCESSLEN 10
458
459#define CSFUNC_ARGS user, channel, argc, argv, cmd
460
461#define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
462#define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
463#define REQUIRE_PARAMS(N) if(argc < (N)) { \
464 reply("MSG_MISSING_PARAMS", argv[0]); \
465 CHANSERV_SYNTAX(); \
466 return 0; }
467
468DECLARE_LIST(dnrList, struct do_not_register *);
469DEFINE_LIST(dnrList, struct do_not_register *);
470
471static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
472
473struct userNode *chanserv;
474dict_t note_types;
475int off_channel;
476static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
477static struct log_type *CS_LOG;
478
479static struct
480{
481 struct channelList support_channels;
482 struct mod_chanmode default_modes;
483
484 unsigned long db_backup_frequency;
485 unsigned long channel_expire_frequency;
486
487 long info_delay;
488 unsigned int adjust_delay;
489 long channel_expire_delay;
490 unsigned int nodelete_level;
491
492 unsigned int adjust_threshold;
493 int join_flood_threshold;
494
495 unsigned int greeting_length;
496 unsigned int refresh_period;
497
498 unsigned int max_owned;
499 unsigned int max_chan_users;
500 unsigned int max_chan_bans;
501 unsigned int max_userinfo_length;
502
503 struct string_list *set_shows;
504 struct string_list *eightball;
505 struct string_list *old_ban_names;
506
507 const char *ctcp_short_ban_duration;
508 const char *ctcp_long_ban_duration;
509
510 const char *irc_operator_epithet;
511 const char *network_helper_epithet;
512 const char *support_helper_epithet;
513} chanserv_conf;
514
515struct listData
516{
517 struct userNode *user;
518 struct userNode *bot;
519 struct chanNode *channel;
520 const char *search;
521 unsigned short lowest;
522 unsigned short highest;
523 struct userData **users;
524 struct helpfile_table table;
525};
526
527enum note_access_type
528{
529 NOTE_SET_CHANNEL_ACCESS,
530 NOTE_SET_CHANNEL_SETTER,
531 NOTE_SET_PRIVILEGED
532};
533
534enum note_visible_type
535{
536 NOTE_VIS_ALL,
537 NOTE_VIS_CHANNEL_USERS,
538 NOTE_VIS_PRIVILEGED
539};
540
541struct note_type
542{
543 enum note_access_type set_access_type;
544 union {
545 unsigned int min_opserv;
546 unsigned short min_ulevel;
547 } set_access;
548 enum note_visible_type visible_type;
549 unsigned int max_length;
550 unsigned int refs;
551 char name[1];
552};
553
554struct note
555{
556 struct note_type *type;
557 char setter[NICKSERV_HANDLE_LEN+1];
558 char note[1];
559};
560
561static unsigned int registered_channels;
562static unsigned int banCount;
563
564static const struct {
565 char *name;
566 char *title;
567 unsigned short level;
568 char ch;
569} accessLevels[] = {
570 { "peon", "Peon", UL_PEON, '+' },
571 { "op", "Op", UL_OP, '@' },
4048352e 572 { "manager", "Manager", UL_MANAGER, '%' },
d76ed9a9 573 { "coowner", "Coowner", UL_COOWNER, '*' },
574 { "owner", "Owner", UL_OWNER, '!' },
575 { "helper", "BUG:", UL_HELPER, 'X' }
576};
577
578static const struct {
579 char *format_name;
580 char *db_name;
581 unsigned short default_value;
582 unsigned int old_idx;
583 unsigned int old_flag;
584 unsigned short flag_value;
585} levelOptions[] = {
586 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
587 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
588 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
589 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
590 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
591 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
592 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
593 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
594 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
595 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
596 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
597};
598
599struct charOptionValues {
600 char value;
601 char *format_name;
602} protectValues[] = {
603 { 'a', "CSMSG_PROTECT_ALL" },
604 { 'e', "CSMSG_PROTECT_EQUAL" },
605 { 'l', "CSMSG_PROTECT_LOWER" },
606 { 'n', "CSMSG_PROTECT_NONE" }
607}, toysValues[] = {
608 { 'd', "CSMSG_TOYS_DISABLED" },
609 { 'n', "CSMSG_TOYS_PRIVATE" },
610 { 'p', "CSMSG_TOYS_PUBLIC" }
611}, topicRefreshValues[] = {
612 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
613 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
614 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
615 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
616 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
617}, ctcpReactionValues[] = {
618 { 'k', "CSMSG_CTCPREACTION_KICK" },
619 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
620 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
621 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
622};
623
624static const struct {
625 char *format_name;
626 char *db_name;
627 char default_value;
628 unsigned int old_idx;
629 unsigned char count;
630 struct charOptionValues *values;
631} charOptions[] = {
632 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
633 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
634 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
635 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
636};
637
638struct userData *helperList;
639struct chanData *channelList;
640static struct module *chanserv_module;
641static unsigned int userCount;
642
643#define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
644#define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
645#define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
646
647unsigned short
648user_level_from_name(const char *name, unsigned short clamp_level)
649{
650 unsigned int level = 0, ii;
651 if(isdigit(name[0]))
652 level = strtoul(name, NULL, 10);
653 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
654 if(!irccasecmp(name, accessLevels[ii].name))
655 level = accessLevels[ii].level;
656 if(level > clamp_level)
657 return 0;
658 return level;
659}
660
661int
662parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
663{
664 char *sep;
665 *minl = strtoul(arg, &sep, 10);
666 if(*sep == '\0')
667 {
668 *maxl = *minl;
669 return 1;
670 }
671 else if(*sep == '-')
672 {
673 *maxl = strtoul(sep+1, &sep, 10);
674 return *sep == '\0';
675 }
676 else
677 return 0;
678}
679
680struct userData*
681_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
682{
683 struct userData *uData, **head;
684
685 if(!channel || !handle)
686 return NULL;
687
688 if(override && HANDLE_FLAGGED(handle, HELPING)
689 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
690 {
691 for(uData = helperList;
692 uData && uData->handle != handle;
693 uData = uData->next);
694
695 if(!uData)
696 {
697 uData = calloc(1, sizeof(struct userData));
698 uData->handle = handle;
699
700 uData->access = UL_HELPER;
701 uData->seen = 0;
702
703 uData->info = NULL;
704
705 uData->prev = NULL;
706 uData->next = helperList;
707 if(helperList)
708 helperList->prev = uData;
709 helperList = uData;
710 }
711
712 head = &helperList;
713 }
714 else
715 {
716 for(uData = channel->users; uData; uData = uData->next)
717 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
718 break;
719
720 head = &(channel->users);
721 }
722
723 if(uData && (uData != *head))
724 {
725 /* Shuffle the user to the head of whatever list he was in. */
726 if(uData->next)
727 uData->next->prev = uData->prev;
728 if(uData->prev)
729 uData->prev->next = uData->next;
730
731 uData->prev = NULL;
732 uData->next = *head;
733
734 if(*head)
735 (**head).prev = uData;
736 *head = uData;
737 }
738
739 return uData;
740}
741
742/* Returns non-zero if user has at least the minimum access.
743 * exempt_owner is set when handling !set, so the owner can set things
744 * to/from >500.
745 */
746int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
747{
748 struct userData *uData;
749 struct chanData *cData = channel->channel_info;
750 unsigned short minimum = cData->lvlOpts[opt];
751 if(!minimum)
752 return 1;
753 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
754 if(!uData)
755 return 0;
756 if(minimum <= uData->access)
757 return 1;
758 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
759 return 1;
760 return 0;
761}
762
763/* Scan for other users authenticated to the same handle
764 still in the channel. If so, keep them listed as present.
765
766 user is optional, if not null, it skips checking that userNode
767 (for the handle_part function) */
768static void
769scan_user_presence(struct userData *uData, struct userNode *user)
770{
771 struct modeNode *mn;
772
773 if(IsSuspended(uData->channel)
774 || IsUserSuspended(uData)
775 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
776 {
777 uData->present = 0;
778 }
779 else
780 {
781 uData->present = 1;
782 uData->seen = now;
783 }
784}
785
786static void
787chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
788{
789 unsigned int eflags, argc;
790 char *argv[4];
791 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
792
793 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
794 if(!channel->channel_info
795 || IsSuspended(channel->channel_info)
796 || IsService(user)
797 || !ircncasecmp(text, "ACTION ", 7))
798 return;
799 /* Figure out the minimum level needed to CTCP the channel */
800 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
801 return;
802 /* We need to enforce against them; do so. */
803 eflags = 0;
804 argv[0] = text;
805 argv[1] = user->nick;
806 argc = 2;
807 if(GetUserMode(channel, user))
808 eflags |= ACTION_KICK;
809 switch(channel->channel_info->chOpts[chCTCPReaction]) {
810 default: case 'k': /* just do the kick */ break;
811 case 'b':
812 eflags |= ACTION_BAN;
813 break;
814 case 't':
815 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
816 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
817 break;
818 case 'T':
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
821 break;
822 }
823 argv[argc++] = bad_ctcp_reason;
824 eject_user(chanserv, channel, argc, argv, NULL, eflags);
825}
826
827struct note_type *
828chanserv_create_note_type(const char *name)
829{
830 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
831 strcpy(ntype->name, name);
832 ntype->refs = 1;
833 dict_insert(note_types, ntype->name, ntype);
834 return ntype;
835}
836
837static void
838chanserv_deref_note_type(void *data)
839{
840 struct note_type *ntype = data;
841
842 if(--ntype->refs > 0)
843 return;
844 free(ntype);
845}
846
847static void
848chanserv_flush_note_type(struct note_type *ntype)
849{
850 struct chanData *cData;
851 for(cData = channelList; cData; cData = cData->next)
852 dict_remove(cData->notes, ntype->name);
853}
854
855static void
856chanserv_truncate_notes(struct note_type *ntype)
857{
858 struct chanData *cData;
859 struct note *note;
860 unsigned int size = sizeof(*note) + ntype->max_length;
861
862 for(cData = channelList; cData; cData = cData->next) {
863 note = dict_find(cData->notes, ntype->name, NULL);
864 if(!note)
865 continue;
866 if(strlen(note->note) <= ntype->max_length)
867 continue;
868 dict_remove2(cData->notes, ntype->name, 1);
869 note = realloc(note, size);
870 note->note[ntype->max_length] = 0;
871 dict_insert(cData->notes, ntype->name, note);
872 }
873}
874
875static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
876
877static struct note *
878chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
879{
880 struct note *note;
881 unsigned int len = strlen(text);
882
883 if(len > type->max_length) len = type->max_length;
884 note = calloc(1, sizeof(*note) + len);
885 note->type = type;
886 strncpy(note->setter, setter, sizeof(note->setter)-1);
887 memcpy(note->note, text, len);
888 note->note[len] = 0;
889 dict_insert(channel->notes, type->name, note);
890 type->refs++;
891 return note;
892}
893
894static void
895chanserv_free_note(void *data)
896{
897 struct note *note = data;
898
899 chanserv_deref_note_type(note->type);
900 assert(note->type->refs > 0); /* must use delnote to remove the type */
901 free(note);
902}
903
904static MODCMD_FUNC(cmd_createnote) {
905 struct note_type *ntype;
906 unsigned int arg = 1, existed = 0, max_length;
907
908 if((ntype = dict_find(note_types, argv[1], NULL)))
909 existed = 1;
910 else
911 ntype = chanserv_create_note_type(argv[arg]);
912 if(!irccasecmp(argv[++arg], "privileged"))
913 {
914 arg++;
915 ntype->set_access_type = NOTE_SET_PRIVILEGED;
916 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
917 }
918 else if(!irccasecmp(argv[arg], "channel"))
919 {
920 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
921 if(!ulvl)
922 {
923 reply("CSMSG_INVALID_ACCESS", argv[arg]);
924 goto fail;
925 }
926 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
927 ntype->set_access.min_ulevel = ulvl;
928 }
929 else if(!irccasecmp(argv[arg], "setter"))
930 {
931 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
932 }
933 else
934 {
935 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
936 goto fail;
937 }
938
939 if(!irccasecmp(argv[++arg], "privileged"))
940 ntype->visible_type = NOTE_VIS_PRIVILEGED;
941 else if(!irccasecmp(argv[arg], "channel_users"))
942 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
943 else if(!irccasecmp(argv[arg], "all"))
944 ntype->visible_type = NOTE_VIS_ALL;
945 else {
946 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
947 goto fail;
948 }
949
950 if((arg+1) >= argc) {
951 reply("MSG_MISSING_PARAMS", argv[0]);
952 goto fail;
953 }
954 max_length = strtoul(argv[++arg], NULL, 0);
955 if(max_length < 20 || max_length > 450)
956 {
957 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
958 goto fail;
959 }
960 if(existed && (max_length < ntype->max_length))
961 {
962 ntype->max_length = max_length;
963 chanserv_truncate_notes(ntype);
964 }
965 ntype->max_length = max_length;
966
967 if(existed)
968 reply("CSMSG_NOTE_MODIFIED", ntype->name);
969 else
970 reply("CSMSG_NOTE_CREATED", ntype->name);
971 return 1;
972
973fail:
974 if(!existed)
975 dict_remove(note_types, ntype->name);
976 return 0;
977}
978
979static MODCMD_FUNC(cmd_removenote) {
980 struct note_type *ntype;
981 int force;
982
983 ntype = dict_find(note_types, argv[1], NULL);
984 force = (argc > 2) && !irccasecmp(argv[2], "force");
985 if(!ntype)
986 {
987 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
988 return 0;
989 }
990 if(ntype->refs > 1)
991 {
992 if(!force)
993 {
994 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
995 return 0;
996 }
997 chanserv_flush_note_type(ntype);
998 }
999 dict_remove(note_types, argv[1]);
1000 reply("CSMSG_NOTE_DELETED", argv[1]);
1001 return 1;
1002}
1003
1004static int
1005mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1006{
1007 if(!orig)
1008 return 0;
1009 if(orig->modes_set & change->modes_clear)
1010 return 1;
1011 if(orig->modes_clear & change->modes_set)
1012 return 1;
1013 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1014 && strcmp(orig->new_key, change->new_key))
1015 return 1;
1016 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1017 && (orig->new_limit != change->new_limit))
1018 return 1;
1019 return 0;
1020}
1021
1022static char max_length_text[MAXLEN+1][16];
1023
1024static struct helpfile_expansion
1025chanserv_expand_variable(const char *variable)
1026{
1027 struct helpfile_expansion exp;
1028
1029 if(!irccasecmp(variable, "notes"))
1030 {
1031 dict_iterator_t it;
1032 exp.type = HF_TABLE;
1033 exp.value.table.length = 1;
1034 exp.value.table.width = 3;
1035 exp.value.table.flags = 0;
1036 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1037 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1038 exp.value.table.contents[0][0] = "Note Type";
1039 exp.value.table.contents[0][1] = "Visibility";
1040 exp.value.table.contents[0][2] = "Max Length";
1041 for(it=dict_first(note_types); it; it=iter_next(it))
1042 {
1043 struct note_type *ntype = iter_data(it);
1044 int row;
1045
1046 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1047 row = exp.value.table.length++;
1048 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1049 exp.value.table.contents[row][0] = ntype->name;
1050 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1051 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1052 "unknown";
1053 if(!max_length_text[ntype->max_length][0])
1054 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1055 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1056 }
1057 return exp;
1058 }
1059
1060 exp.type = HF_STRING;
1061 exp.value.str = NULL;
1062 return exp;
1063}
1064
1065static struct chanData*
1066register_channel(struct chanNode *cNode, char *registrar)
1067{
1068 struct chanData *channel;
1069 enum levelOption lvlOpt;
1070 enum charOption chOpt;
1071
1072 channel = calloc(1, sizeof(struct chanData));
1073
1074 channel->notes = dict_new();
1075 dict_set_free_data(channel->notes, chanserv_free_note);
1076
1077 channel->registrar = strdup(registrar);
1078 channel->registered = now;
1079 channel->visited = now;
1080 channel->limitAdjusted = now;
1081 channel->flags = CHANNEL_DEFAULT_FLAGS;
1082 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1083 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1084 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1085 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1086
1087 channel->prev = NULL;
1088 channel->next = channelList;
1089
1090 if(channelList)
1091 channelList->prev = channel;
1092 channelList = channel;
1093 registered_channels++;
1094
1095 channel->channel = cNode;
1096 LockChannel(cNode);
1097 cNode->channel_info = channel;
1098
1099 return channel;
1100}
1101
1102static struct userData*
1103add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1104{
1105 struct userData *ud;
1106
1107 if(access > UL_OWNER)
1108 return NULL;
1109
1110 ud = calloc(1, sizeof(*ud));
1111 ud->channel = channel;
1112 ud->handle = handle;
1113 ud->seen = seen;
1114 ud->access = access;
1115 ud->info = info ? strdup(info) : NULL;
1116
1117 ud->prev = NULL;
1118 ud->next = channel->users;
1119 if(channel->users)
1120 channel->users->prev = ud;
1121 channel->users = ud;
1122
1123 channel->userCount++;
1124 userCount++;
1125
1126 ud->u_prev = NULL;
1127 ud->u_next = ud->handle->channels;
1128 if(ud->u_next)
1129 ud->u_next->u_prev = ud;
1130 ud->handle->channels = ud;
1131
1132 return ud;
1133}
1134
1135static void unregister_channel(struct chanData *channel, const char *reason);
1136
1137void
1138del_channel_user(struct userData *user, int do_gc)
1139{
1140 struct chanData *channel = user->channel;
1141
1142 channel->userCount--;
1143 userCount--;
1144
1145 if(user->prev)
1146 user->prev->next = user->next;
1147 else
1148 channel->users = user->next;
1149 if(user->next)
1150 user->next->prev = user->prev;
1151
1152 if(user->u_prev)
1153 user->u_prev->u_next = user->u_next;
1154 else
1155 user->handle->channels = user->u_next;
1156 if(user->u_next)
1157 user->u_next->u_prev = user->u_prev;
1158
1159 free(user->info);
1160 free(user);
1161 if(do_gc && !channel->users && !IsProtected(channel))
1162 unregister_channel(channel, "lost all users.");
1163}
1164
1165static void expire_ban(void *data);
1166
1167static struct banData*
1168add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1169{
1170 struct banData *bd;
1171 unsigned int ii, l1, l2;
1172
1173 if(!mask)
1174 return NULL;
1175
1176 bd = malloc(sizeof(struct banData));
1177
1178 bd->channel = channel;
1179 bd->set = set;
1180 bd->triggered = triggered;
1181 bd->expires = expires;
1182
1183 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1184 {
1185 extern const char *hidden_host_suffix;
1186 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1187 char *new_mask;
1188
1189 l1 = strlen(mask);
1190 l2 = strlen(old_name);
1191 if(l2+2 > l1)
1192 continue;
1193 if(irccasecmp(mask + l1 - l2, old_name))
1194 continue;
1195 new_mask = alloca(MAXLEN);
1196 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1197 mask = new_mask;
1198 }
1199 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1200 if(owner)
1201 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1202 bd->reason = reason ? strdup(reason) : NULL;
1203
1204 if(expires)
1205 timeq_add(expires, expire_ban, bd);
1206
1207 bd->prev = NULL;
1208 bd->next = channel->bans;
1209 if(channel->bans)
1210 channel->bans->prev = bd;
1211 channel->bans = bd;
1212 channel->banCount++;
1213 banCount++;
1214
1215 return bd;
1216}
1217
1218static void
1219del_channel_ban(struct banData *ban)
1220{
1221 ban->channel->banCount--;
1222 banCount--;
1223
1224 if(ban->prev)
1225 ban->prev->next = ban->next;
1226 else
1227 ban->channel->bans = ban->next;
1228
1229 if(ban->next)
1230 ban->next->prev = ban->prev;
1231
1232 if(ban->expires)
1233 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1234
1235 if(ban->reason)
1236 free(ban->reason);
1237
1238 free(ban);
1239}
1240
1241static void
1242expire_ban(void *data)
1243{
1244 struct banData *bd = data;
1245 if(!IsSuspended(bd->channel))
1246 {
1247 struct banList bans;
1248 struct mod_chanmode change;
1249 unsigned int ii;
1250 bans = bd->channel->channel->banlist;
1251 mod_chanmode_init(&change);
1252 for(ii=0; ii<bans.used; ii++)
1253 {
1254 if(!strcmp(bans.list[ii]->ban, bd->mask))
1255 {
1256 change.argc = 1;
1257 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1258 change.args[0].hostmask = bd->mask;
1259 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1260 break;
1261 }
1262 }
1263 }
1264 bd->expires = 0;
1265 del_channel_ban(bd);
1266}
1267
1268static void chanserv_expire_suspension(void *data);
1269
1270static void
1271unregister_channel(struct chanData *channel, const char *reason)
1272{
1273 struct mod_chanmode change;
1274 char msgbuf[MAXLEN];
1275
1276 /* After channel unregistration, the following must be cleaned
1277 up:
1278 - Channel information.
1279 - Channel users.
1280 - Channel bans.
1281 - Channel suspension data.
1282 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1283 */
1284
1285 if(!channel)
1286 return;
1287
1288 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1289
1290 mod_chanmode_init(&change);
1291 change.modes_clear |= MODE_REGISTERED;
1292 mod_chanmode_announce(chanserv, channel->channel, &change);
1293
1294 while(channel->users)
1295 del_channel_user(channel->users, 0);
1296
1297 while(channel->bans)
1298 del_channel_ban(channel->bans);
1299
1300 free(channel->topic);
1301 free(channel->registrar);
1302 free(channel->greeting);
1303 free(channel->user_greeting);
1304 free(channel->topic_mask);
1305
1306 if(channel->prev)
1307 channel->prev->next = channel->next;
1308 else
1309 channelList = channel->next;
1310
1311 if(channel->next)
1312 channel->next->prev = channel->prev;
1313
1314 if(channel->suspended)
1315 {
1316 struct chanNode *cNode = channel->channel;
1317 struct suspended *suspended, *next_suspended;
1318
1319 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1320 {
1321 next_suspended = suspended->previous;
1322 free(suspended->suspender);
1323 free(suspended->reason);
1324 if(suspended->expires)
1325 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1326 free(suspended);
1327 }
1328
1329 if(cNode)
1330 cNode->channel_info = NULL;
1331 }
1332 channel->channel->channel_info = NULL;
1333
1334 dict_delete(channel->notes);
1335 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1336 if(!IsSuspended(channel))
1337 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1338 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1339 UnlockChannel(channel->channel);
1340 free(channel);
1341 registered_channels--;
1342}
1343
1344static void
1345expire_channels(UNUSED_ARG(void *data))
1346{
1347 struct chanData *channel, *next;
1348 struct userData *user;
1349 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1350
1351 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1352 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1353
1354 for(channel = channelList; channel; channel = next)
1355 {
1356 next = channel->next;
1357
1358 /* See if the channel can be expired. */
1359 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1360 || IsProtected(channel))
1361 continue;
1362
1363 /* Make sure there are no high-ranking users still in the channel. */
1364 for(user=channel->users; user; user=user->next)
1365 if(user->present && (user->access >= UL_PRESENT))
1366 break;
1367 if(user)
1368 continue;
1369
1370 /* Unregister the channel */
1371 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1372 unregister_channel(channel, "registration expired.");
1373 }
1374
1375 if(chanserv_conf.channel_expire_frequency)
1376 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1377}
1378
1379static int
1380protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1381{
1382 char protect = channel->chOpts[chProtect];
1383 struct userData *cs_victim, *cs_aggressor;
1384
1385 /* Don't protect if no one is to be protected, someone is attacking
1386 himself, or if the aggressor is an IRC Operator. */
1387 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1388 return 0;
1389
1390 /* Don't protect if the victim isn't authenticated (because they
1391 can't be a channel user), unless we are to protect non-users
1392 also. */
1393 cs_victim = GetChannelAccess(channel, victim->handle_info);
1394 if(protect != 'a' && !cs_victim)
1395 return 0;
1396
1397 /* Protect if the aggressor isn't a user because at this point,
1398 the aggressor can only be less than or equal to the victim. */
1399 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1400 if(!cs_aggressor)
1401 return 1;
1402
1403 /* If the aggressor was a user, then the victim can't be helped. */
1404 if(!cs_victim)
1405 return 0;
1406
1407 switch(protect)
1408 {
1409 case 'l':
1410 if(cs_victim->access > cs_aggressor->access)
1411 return 1;
1412 break;
1413 case 'a':
1414 case 'e':
1415 if(cs_victim->access >= cs_aggressor->access)
1416 return 1;
1417 break;
1418 }
1419
1420 return 0;
1421}
1422
1423static int
1424validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1425{
1426 struct chanData *cData = channel->channel_info;
1427 struct userData *cs_victim;
1428
1429 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1430 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1431 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1432 {
1433 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1434 return 0;
1435 }
1436
1437 return 1;
1438}
1439
1440static int
1441validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1442{
1443 if(IsService(victim))
1444 {
1445 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1446 return 0;
1447 }
1448
1449 if(protect_user(victim, user, channel->channel_info))
1450 {
1451 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1452 return 0;
1453 }
1454
1455 return 1;
1456}
1457
1458static struct do_not_register *
1459chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1460{
1461 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1462 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1463 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1464 strcpy(dnr->reason, reason);
1465 dnr->set = now;
1466 if(dnr->chan_name[0] == '*')
1467 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1468 else if(strpbrk(dnr->chan_name, "*?"))
1469 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1470 else
1471 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1472 return dnr;
1473}
1474
1475static struct dnrList
1476chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1477{
1478 struct dnrList list;
1479 dict_iterator_t it;
1480 struct do_not_register *dnr;
1481
1482 dnrList_init(&list);
1483 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1484 dnrList_append(&list, dnr);
1485 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1486 dnrList_append(&list, dnr);
1487 if(chan_name)
1488 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1489 if(match_ircglob(chan_name, iter_key(it)))
1490 dnrList_append(&list, iter_data(it));
1491 return list;
1492}
1493
1494static unsigned int
1495chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1496{
1497 struct dnrList list;
1498 struct do_not_register *dnr;
1499 unsigned int ii;
1500 char buf[INTERVALLEN];
1501
1502 list = chanserv_find_dnrs(chan_name, handle);
1503 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1504 {
1505 dnr = list.list[ii];
1506 if(dnr->set)
1507 {
1508 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1509 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1510 }
1511 else
1512 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1513 }
1514 if(ii < list.used)
1515 reply("CSMSG_MORE_DNRS", list.used - ii);
1516 free(list.list);
1517 return ii;
1518}
1519
1520struct do_not_register *
1521chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1522{
1523 struct do_not_register *dnr;
1524 dict_iterator_t it;
1525
1526 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1527 return dnr;
1528 if(chan_name)
1529 {
1530 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1531 return dnr;
1532 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1533 if(match_ircglob(chan_name, iter_key(it)))
1534 return iter_data(it);
1535 }
1536 return NULL;
1537}
1538
1539static CHANSERV_FUNC(cmd_noregister)
1540{
1541 const char *target;
1542 struct do_not_register *dnr;
1543 char buf[INTERVALLEN];
1544 unsigned int matches;
1545
1546 if(argc < 2)
1547 {
1548 dict_iterator_t it;
1549
1550 reply("CSMSG_DNR_SEARCH_RESULTS");
1551 matches = 0;
1552 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1553 {
1554 dnr = iter_data(it);
1555 if(dnr->set)
1556 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1557 else
1558 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1559 matches++;
1560 }
1561 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1562 {
1563 dnr = iter_data(it);
1564 if(dnr->set)
1565 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1566 else
1567 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1568 matches++;
1569 }
1570 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1571 {
1572 dnr = iter_data(it);
1573 if(dnr->set)
1574 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1575 else
1576 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1577 matches++;
1578 }
1579
1580 if(matches)
1581 reply("MSG_MATCH_COUNT", matches);
1582 else
1583 reply("MSG_NO_MATCHES");
1584 return 0;
1585 }
1586
1587 target = argv[1];
1588
1589 if(!IsChannelName(target) && (*target != '*'))
1590 {
1591 reply("CSMSG_NOT_DNR", target);
1592 return 0;
1593 }
1594
1595 if(argc > 2)
1596 {
1597 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1598 if((*target == '*') && !get_handle_info(target + 1))
1599 {
1600 reply("MSG_HANDLE_UNKNOWN", target + 1);
1601 return 0;
1602 }
1603 chanserv_add_dnr(target, user->handle_info->handle, reason);
1604 reply("CSMSG_NOREGISTER_CHANNEL", target);
1605 return 1;
1606 }
1607
1608 reply("CSMSG_DNR_SEARCH_RESULTS");
1609 if(*target == '*')
1610 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1611 else
1612 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1613 if(!matches)
1614 reply("MSG_NO_MATCHES");
1615 return 0;
1616}
1617
1618static CHANSERV_FUNC(cmd_allowregister)
1619{
1620 const char *chan_name = argv[1];
1621
1622 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1623 {
1624 dict_remove(handle_dnrs, chan_name+1);
1625 reply("CSMSG_DNR_REMOVED", chan_name);
1626 }
1627 else if(dict_find(plain_dnrs, chan_name, NULL))
1628 {
1629 dict_remove(plain_dnrs, chan_name);
1630 reply("CSMSG_DNR_REMOVED", chan_name);
1631 }
1632 else if(dict_find(mask_dnrs, chan_name, NULL))
1633 {
1634 dict_remove(mask_dnrs, chan_name);
1635 reply("CSMSG_DNR_REMOVED", chan_name);
1636 }
1637 else
1638 {
1639 reply("CSMSG_NO_SUCH_DNR", chan_name);
1640 return 0;
1641 }
1642 return 1;
1643}
1644
1645unsigned int
1646chanserv_get_owned_count(struct handle_info *hi)
1647{
1648 struct userData *cList;
1649 unsigned int owned;
1650
1651 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1652 if(cList->access == UL_OWNER)
1653 owned++;
1654 return owned;
1655}
1656
1657static CHANSERV_FUNC(cmd_register)
1658{
1659 struct mod_chanmode *change;
1660 struct handle_info *handle;
1661 struct chanData *cData;
1662 struct modeNode *mn;
1663 char reason[MAXLEN];
1664 char *chan_name;
1665 unsigned int new_channel, force=0;
1666 struct do_not_register *dnr;
1667
1668 if(channel)
1669 {
1670 if(channel->channel_info)
1671 {
1672 reply("CSMSG_ALREADY_REGGED", channel->name);
1673 return 0;
1674 }
1675
1676 if(channel->bad_channel)
1677 {
1678 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1679 return 0;
1680 }
1681
1682 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1683 {
1684 reply("CSMSG_MUST_BE_OPPED", channel->name);
1685 return 0;
1686 }
1687
1688 new_channel = 0;
1689 chan_name = channel->name;
1690 }
1691 else
1692 {
1693 if((argc < 2) || !IsChannelName(argv[1]))
1694 {
1695 reply("MSG_NOT_CHANNEL_NAME");
1696 return 0;
1697 }
1698
1699 if(opserv_bad_channel(argv[1]))
1700 {
1701 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1702 return 0;
1703 }
1704
1705 new_channel = 1;
1706 chan_name = argv[1];
1707 }
1708
1709 if(argc >= (new_channel+2))
1710 {
1711 if(!IsHelping(user))
1712 {
1713 reply("CSMSG_PROXY_FORBIDDEN");
1714 return 0;
1715 }
1716
1717 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1718 return 0;
1719 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1720 dnr = chanserv_is_dnr(chan_name, handle);
1721 }
1722 else
1723 {
1724 handle = user->handle_info;
1725 dnr = chanserv_is_dnr(chan_name, handle);
1726 }
1727 if(dnr && !force)
1728 {
1729 if(!IsHelping(user))
1730 reply("CSMSG_DNR_CHANNEL", chan_name);
1731 else
1732 chanserv_show_dnrs(user, cmd, chan_name, handle);
1733 return 0;
1734 }
1735
1736 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1737 {
1738 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1739 return 0;
1740 }
1741
1742 if(new_channel)
1743 channel = AddChannel(argv[1], now, NULL, NULL);
1744
1745 cData = register_channel(channel, user->handle_info->handle);
1746 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1747 cData->modes = chanserv_conf.default_modes;
1748 change = mod_chanmode_dup(&cData->modes, 1);
1749 change->args[change->argc].mode = MODE_CHANOP;
1750 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1751 change->argc++;
1752 mod_chanmode_announce(chanserv, channel, change);
1753 mod_chanmode_free(change);
1754
1755 /* Initialize the channel's max user record. */
1756 cData->max = channel->members.used;
1757
1758 if(handle != user->handle_info)
1759 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1760 else
1761 reply("CSMSG_REG_SUCCESS", channel->name);
1762
1763 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1764 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1765 return 1;
1766}
1767
1768static const char *
1769make_confirmation_string(struct userData *uData)
1770{
1771 static char strbuf[16];
1772 char *src;
1773 unsigned int accum;
1774
1775 accum = 0;
1776 for(src = uData->handle->handle; *src; )
1777 accum = accum * 31 + toupper(*src++);
1778 if(uData->channel)
1779 for(src = uData->channel->channel->name; *src; )
1780 accum = accum * 31 + toupper(*src++);
1781 sprintf(strbuf, "%08x", accum);
1782 return strbuf;
1783}
1784
1785static CHANSERV_FUNC(cmd_unregister)
1786{
1787 char *name;
1788 char reason[MAXLEN];
1789 struct chanData *cData;
1790 struct userData *uData;
1791
1792 cData = channel->channel_info;
1793 if(!cData)
1794 {
1795 reply("CSMSG_NOT_REGISTERED", channel->name);
1796 return 0;
1797 }
1798
1799 uData = GetChannelUser(cData, user->handle_info);
1800 if(!uData || (uData->access < UL_OWNER))
1801 {
1802 reply("CSMSG_NO_ACCESS");
1803 return 0;
1804 }
1805
1806 if(IsProtected(cData))
1807 {
1808 reply("CSMSG_UNREG_NODELETE", channel->name);
1809 return 0;
1810 }
1811
1812 if(!IsHelping(user))
1813 {
1814 const char *confirm_string;
1815 if(IsSuspended(cData))
1816 {
1817 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1818 return 0;
1819 }
1820 confirm_string = make_confirmation_string(uData);
1821 if((argc < 2) || strcmp(argv[1], confirm_string))
1822 {
1823 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1824 return 0;
1825 }
1826 }
1827
1828 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1829 name = strdup(channel->name);
1830 unregister_channel(cData, reason);
1831 reply("CSMSG_UNREG_SUCCESS", name);
1832 free(name);
1833 return 1;
1834}
1835
1836static CHANSERV_FUNC(cmd_move)
1837{
1838 struct chanNode *target;
1839 struct modeNode *mn;
1840 struct userData *uData;
1841 char reason[MAXLEN];
1842 struct do_not_register *dnr;
1843
1844 REQUIRE_PARAMS(2);
1845
1846 if(IsProtected(channel->channel_info))
1847 {
1848 reply("CSMSG_MOVE_NODELETE", channel->name);
1849 return 0;
1850 }
1851
1852 if(!IsChannelName(argv[1]))
1853 {
1854 reply("MSG_NOT_CHANNEL_NAME");
1855 return 0;
1856 }
1857
1858 if(opserv_bad_channel(argv[1]))
1859 {
1860 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1861 return 0;
1862 }
1863
1864 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1865 {
1866 for(uData = channel->channel_info->users; uData; uData = uData->next)
1867 {
1868 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1869 {
1870 if(!IsHelping(user))
1871 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1872 else
1873 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1874 return 0;
1875 }
1876 }
1877 }
1878
1879 if(!(target = GetChannel(argv[1])))
1880 {
1881 target = AddChannel(argv[1], now, NULL, NULL);
1882 if(!IsSuspended(channel->channel_info))
1883 AddChannelUser(chanserv, target);
1884 }
1885 else if(target->channel_info)
1886 {
1887 reply("CSMSG_ALREADY_REGGED", target->name);
1888 return 0;
1889 }
1890 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1891 && !IsHelping(user))
1892 {
1893 reply("CSMSG_MUST_BE_OPPED", target->name);
1894 return 0;
1895 }
1896 else if(!IsSuspended(channel->channel_info))
1897 {
1898 struct mod_chanmode change;
1899 mod_chanmode_init(&change);
1900 change.argc = 1;
1901 change.args[0].mode = MODE_CHANOP;
1902 change.args[0].member = AddChannelUser(chanserv, target);
1903 mod_chanmode_announce(chanserv, target, &change);
1904 }
1905
1906 /* Move the channel_info to the target channel; it
1907 shouldn't be necessary to clear timeq callbacks
1908 for the old channel. */
1909 target->channel_info = channel->channel_info;
1910 target->channel_info->channel = target;
1911 channel->channel_info = NULL;
1912
1913 reply("CSMSG_MOVE_SUCCESS", target->name);
1914
1915 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1916 if(!IsSuspended(target->channel_info))
1917 {
1918 char reason2[MAXLEN];
1919 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1920 DelChannelUser(chanserv, channel, reason2, 0);
1921 }
1922 UnlockChannel(channel);
1923 LockChannel(target);
1924 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1925 return 1;
1926}
1927
1928static void
1929merge_users(struct chanData *source, struct chanData *target)
1930{
1931 struct userData *suData, *tuData, *next;
1932 dict_iterator_t it;
1933 dict_t merge;
1934
1935 merge = dict_new();
1936
1937 /* Insert the source's users into the scratch area. */
1938 for(suData = source->users; suData; suData = suData->next)
1939 dict_insert(merge, suData->handle->handle, suData);
1940
1941 /* Iterate through the target's users, looking for
1942 users common to both channels. The lower access is
1943 removed from either the scratch area or target user
1944 list. */
1945 for(tuData = target->users; tuData; tuData = next)
1946 {
1947 struct userData *choice;
1948
1949 next = tuData->next;
1950
1951 /* If a source user exists with the same handle as a target
1952 channel's user, resolve the conflict by removing one. */
1953 suData = dict_find(merge, tuData->handle->handle, NULL);
1954 if(!suData)
1955 continue;
1956
1957 /* Pick the data we want to keep. */
1958 /* If the access is the same, use the later seen time. */
1959 if(suData->access == tuData->access)
1960 choice = (suData->seen > tuData->seen) ? suData : tuData;
1961 else /* Otherwise, keep the higher access level. */
1962 choice = (suData->access > tuData->access) ? suData : tuData;
1963
1964 /* Remove the user that wasn't picked. */
1965 if(choice == tuData)
1966 {
1967 dict_remove(merge, suData->handle->handle);
1968 del_channel_user(suData, 0);
1969 }
1970 else
1971 del_channel_user(tuData, 0);
1972 }
1973
1974 /* Move the remaining users to the target channel. */
1975 for(it = dict_first(merge); it; it = iter_next(it))
1976 {
1977 suData = iter_data(it);
1978
1979 /* Insert the user into the target channel's linked list. */
1980 suData->prev = NULL;
1981 suData->next = target->users;
1982 suData->channel = target;
1983
1984 if(target->users)
1985 target->users->prev = suData;
1986 target->users = suData;
1987
1988 /* Update the user counts for the target channel; the
1989 source counts are left alone. */
1990 target->userCount++;
1991 }
1992
1993 /* Possible to assert (source->users == NULL) here. */
1994 source->users = NULL;
1995 dict_delete(merge);
1996}
1997
1998static void
1999merge_bans(struct chanData *source, struct chanData *target)
2000{
2001 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2002
2003 /* Hold on to the original head of the target ban list
2004 to avoid comparing source bans with source bans. */
2005 tFront = target->bans;
2006
2007 /* Perform a totally expensive O(n*m) merge, ick. */
2008 for(sbData = source->bans; sbData; sbData = sNext)
2009 {
2010 /* Flag to track whether the ban's been moved
2011 to the destination yet. */
2012 int moved = 0;
2013
2014 /* Possible to assert (sbData->prev == NULL) here. */
2015 sNext = sbData->next;
2016
2017 for(tbData = tFront; tbData; tbData = tNext)
2018 {
2019 tNext = tbData->next;
2020
2021 /* Perform two comparisons between each source
2022 and target ban, conflicts are resolved by
2023 keeping the broader ban and copying the later
2024 expiration and triggered time. */
2025 if(match_ircglobs(tbData->mask, sbData->mask))
2026 {
2027 /* There is a broader ban in the target channel that
2028 overrides one in the source channel; remove the
2029 source ban and break. */
2030 if(sbData->expires > tbData->expires)
2031 tbData->expires = sbData->expires;
2032 if(sbData->triggered > tbData->triggered)
2033 tbData->triggered = sbData->triggered;
2034 del_channel_ban(sbData);
2035 break;
2036 }
2037 else if(match_ircglobs(sbData->mask, tbData->mask))
2038 {
2039 /* There is a broader ban in the source channel that
2040 overrides one in the target channel; remove the
2041 target ban, fall through and move the source over. */
2042 if(tbData->expires > sbData->expires)
2043 sbData->expires = tbData->expires;
2044 if(tbData->triggered > sbData->triggered)
2045 sbData->triggered = tbData->triggered;
2046 if(tbData == tFront)
2047 tFront = tNext;
2048 del_channel_ban(tbData);
2049 }
2050
2051 /* Source bans can override multiple target bans, so
2052 we allow a source to run through this loop multiple
2053 times, but we can only move it once. */
2054 if(moved)
2055 continue;
2056 moved = 1;
2057
2058 /* Remove the source ban from the source ban list. */
2059 if(sbData->next)
2060 sbData->next->prev = sbData->prev;
2061
2062 /* Modify the source ban's associated channel. */
2063 sbData->channel = target;
2064
2065 /* Insert the ban into the target channel's linked list. */
2066 sbData->prev = NULL;
2067 sbData->next = target->bans;
2068
2069 if(target->bans)
2070 target->bans->prev = sbData;
2071 target->bans = sbData;
2072
2073 /* Update the user counts for the target channel. */
2074 target->banCount++;
2075 }
2076 }
2077
2078 /* Possible to assert (source->bans == NULL) here. */
2079 source->bans = NULL;
2080}
2081
2082static void
2083merge_data(struct chanData *source, struct chanData *target)
2084{
2085 if(source->visited > target->visited)
2086 target->visited = source->visited;
2087}
2088
2089static void
2090merge_channel(struct chanData *source, struct chanData *target)
2091{
2092 merge_users(source, target);
2093 merge_bans(source, target);
2094 merge_data(source, target);
2095}
2096
2097static CHANSERV_FUNC(cmd_merge)
2098{
2099 struct userData *target_user;
2100 struct chanNode *target;
2101 char reason[MAXLEN];
2102
2103 REQUIRE_PARAMS(2);
2104
2105 /* Make sure the target channel exists and is registered to the user
2106 performing the command. */
2107 if(!(target = GetChannel(argv[1])))
2108 {
2109 reply("MSG_INVALID_CHANNEL");
2110 return 0;
2111 }
2112
2113 if(!target->channel_info)
2114 {
2115 reply("CSMSG_NOT_REGISTERED", target->name);
2116 return 0;
2117 }
2118
2119 if(IsProtected(channel->channel_info))
2120 {
2121 reply("CSMSG_MERGE_NODELETE");
2122 return 0;
2123 }
2124
2125 if(IsSuspended(target->channel_info))
2126 {
2127 reply("CSMSG_MERGE_SUSPENDED");
2128 return 0;
2129 }
2130
2131 if(channel == target)
2132 {
2133 reply("CSMSG_MERGE_SELF");
2134 return 0;
2135 }
2136
2137 target_user = GetChannelUser(target->channel_info, user->handle_info);
2138 if(!target_user || (target_user->access < UL_OWNER))
2139 {
2140 reply("CSMSG_MERGE_NOT_OWNER");
2141 return 0;
2142 }
2143
2144 /* Merge the channel structures and associated data. */
2145 merge_channel(channel->channel_info, target->channel_info);
2146 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2147 unregister_channel(channel->channel_info, reason);
2148 reply("CSMSG_MERGE_SUCCESS", target->name);
2149 return 1;
2150}
2151
2152static CHANSERV_FUNC(cmd_opchan)
2153{
2154 struct mod_chanmode change;
2155 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2156 {
2157 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2158 return 0;
2159 }
2160 channel->channel_info->may_opchan = 0;
2161 mod_chanmode_init(&change);
2162 change.argc = 1;
2163 change.args[0].mode = MODE_CHANOP;
2164 change.args[0].member = GetUserMode(channel, chanserv);
2165 mod_chanmode_announce(chanserv, channel, &change);
2166 reply("CSMSG_OPCHAN_DONE", channel->name);
2167 return 1;
2168}
2169
2170static CHANSERV_FUNC(cmd_adduser)
2171{
2172 struct userData *actee;
2173 struct userData *actor;
2174 struct handle_info *handle;
2175 unsigned short access;
2176
2177 REQUIRE_PARAMS(3);
2178
2179 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2180 {
2181 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2182 return 0;
2183 }
2184
2185 access = user_level_from_name(argv[2], UL_OWNER);
2186 if(!access)
2187 {
2188 reply("CSMSG_INVALID_ACCESS", argv[2]);
2189 return 0;
2190 }
2191
2192 actor = GetChannelUser(channel->channel_info, user->handle_info);
2193 if(actor->access <= access)
2194 {
2195 reply("CSMSG_NO_BUMP_ACCESS");
2196 return 0;
2197 }
2198
2199 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2200 return 0;
2201
2202 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2203 {
2204 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2205 return 0;
2206 }
2207
2208 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2209 scan_user_presence(actee, NULL);
2210 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2211 return 1;
2212}
2213
2214static CHANSERV_FUNC(cmd_clvl)
2215{
2216 struct handle_info *handle;
2217 struct userData *victim;
2218 struct userData *actor;
2219 unsigned short new_access;
2220 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2221
2222 REQUIRE_PARAMS(3);
2223
2224 actor = GetChannelUser(channel->channel_info, user->handle_info);
2225
2226 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2227 return 0;
2228
2229 if(handle == user->handle_info && !privileged)
2230 {
2231 reply("CSMSG_NO_SELF_CLVL");
2232 return 0;
2233 }
2234
2235 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2236 {
2237 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2238 return 0;
2239 }
2240
2241 if(actor->access <= victim->access && !privileged)
2242 {
2243 reply("MSG_USER_OUTRANKED", handle->handle);
2244 return 0;
2245 }
2246
2247 new_access = user_level_from_name(argv[2], UL_OWNER);
2248
2249 if(!new_access)
2250 {
2251 reply("CSMSG_INVALID_ACCESS", argv[2]);
2252 return 0;
2253 }
2254
2255 if(new_access >= actor->access && !privileged)
2256 {
2257 reply("CSMSG_NO_BUMP_ACCESS");
2258 return 0;
2259 }
2260
2261 victim->access = new_access;
2262 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2263 return 1;
2264}
2265
2266static CHANSERV_FUNC(cmd_deluser)
2267{
2268 struct handle_info *handle;
2269 struct userData *victim;
2270 struct userData *actor;
2271 unsigned short access;
2272 char *chan_name;
2273
2274 REQUIRE_PARAMS(2);
2275
2276 actor = GetChannelUser(channel->channel_info, user->handle_info);
2277
2278 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2279 return 0;
2280
2281 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2282 {
2283 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2284 return 0;
2285 }
2286
2287 if(argc > 2)
2288 {
2289 access = user_level_from_name(argv[1], UL_OWNER);
2290 if(!access)
2291 {
2292 reply("CSMSG_INVALID_ACCESS", argv[1]);
2293 return 0;
2294 }
2295 if(access != victim->access)
2296 {
2297 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2298 return 0;
2299 }
2300 }
2301 else
2302 {
2303 access = victim->access;
2304 }
2305
2306 if((actor->access <= victim->access) && !IsHelping(user))
2307 {
2308 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2309 return 0;
2310 }
2311
2312 chan_name = strdup(channel->name);
2313 del_channel_user(victim, 1);
2314 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2315 free(chan_name);
2316 return 1;
2317}
2318
2319static int
2320cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2321{
2322 struct userData *actor, *uData, *next;
2323
2324 actor = GetChannelUser(channel->channel_info, user->handle_info);
2325
2326 if(min_access > max_access)
2327 {
2328 reply("CSMSG_BAD_RANGE", min_access, max_access);
2329 return 0;
2330 }
2331
2332 if((actor->access <= max_access) && !IsHelping(user))
2333 {
2334 reply("CSMSG_NO_ACCESS");
2335 return 0;
2336 }
2337
2338 for(uData = channel->channel_info->users; uData; uData = next)
2339 {
2340 next = uData->next;
2341
2342 if((uData->access >= min_access)
2343 && (uData->access <= max_access)
2344 && match_ircglob(uData->handle->handle, mask))
2345 del_channel_user(uData, 1);
2346 }
2347
2348 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2349 return 1;
2350}
2351
2352static CHANSERV_FUNC(cmd_mdelowner)
2353{
2354 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2355}
2356
2357static CHANSERV_FUNC(cmd_mdelcoowner)
2358{
2359 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2360}
2361
4048352e 2362static CHANSERV_FUNC(cmd_mdelmanager)
d76ed9a9 2363{
4048352e 2364 return cmd_mdel_user(user, channel, UL_MANAGER, UL_MANAGER, argv[1], cmd);
d76ed9a9 2365}
2366
2367static CHANSERV_FUNC(cmd_mdelop)
2368{
2369 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2370}
2371
2372static CHANSERV_FUNC(cmd_mdelpeon)
2373{
2374 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2375}
2376
2377static int
2378cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2379{
2380 struct banData *bData, *next;
2381 char interval[INTERVALLEN];
2382 unsigned int count;
2383 time_t limit;
2384
2385 count = 0;
2386 limit = now - duration;
2387 for(bData = channel->channel_info->bans; bData; bData = next)
2388 {
2389 next = bData->next;
2390
2391 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2392 continue;
2393
2394 del_channel_ban(bData);
2395 count++;
2396 }
2397
2398 intervalString(interval, duration, user->handle_info);
2399 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2400 return 1;
2401}
2402
2403static int
2404cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2405{
2406 struct userData *actor, *uData, *next;
2407 char interval[INTERVALLEN];
2408 unsigned int count;
2409 time_t limit;
2410
2411 actor = GetChannelUser(channel->channel_info, user->handle_info);
2412 if(min_access > max_access)
2413 {
2414 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2415 return 0;
2416 }
2417
2418 if((actor->access <= max_access) && !IsHelping(user))
2419 {
2420 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2421 return 0;
2422 }
2423
2424 count = 0;
2425 limit = now - duration;
2426 for(uData = channel->channel_info->users; uData; uData = next)
2427 {
2428 next = uData->next;
2429
2430 if((uData->seen > limit) || uData->present)
2431 continue;
2432
2433 if(((uData->access >= min_access) && (uData->access <= max_access))
2434 || (!max_access && (uData->access < actor->access)))
2435 {
2436 del_channel_user(uData, 1);
2437 count++;
2438 }
2439 }
2440
2441 if(!max_access)
2442 {
2443 min_access = 1;
2444 max_access = UL_OWNER;
2445 }
2446 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2447 return 1;
2448}
2449
2450static CHANSERV_FUNC(cmd_trim)
2451{
2452 unsigned long duration;
2453 unsigned short min_level, max_level;
2454
2455 REQUIRE_PARAMS(3);
2456
2457 duration = ParseInterval(argv[2]);
2458 if(duration < 60)
2459 {
2460 reply("CSMSG_CANNOT_TRIM");
2461 return 0;
2462 }
2463
2464 if(!irccasecmp(argv[1], "bans"))
2465 {
2466 cmd_trim_bans(user, channel, duration);
2467 return 1;
2468 }
2469 else if(!irccasecmp(argv[1], "users"))
2470 {
2471 cmd_trim_users(user, channel, 0, 0, duration);
2472 return 1;
2473 }
2474 else if(parse_level_range(&min_level, &max_level, argv[1]))
2475 {
2476 cmd_trim_users(user, channel, min_level, max_level, duration);
2477 return 1;
2478 }
2479 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2480 {
2481 cmd_trim_users(user, channel, min_level, min_level, duration);
2482 return 1;
2483 }
2484 else
2485 {
2486 reply("CSMSG_INVALID_TRIM", argv[1]);
2487 return 0;
2488 }
2489}
2490
2491/* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2492 to the user. cmd_all takes advantage of this. */
2493static CHANSERV_FUNC(cmd_up)
2494{
2495 struct mod_chanmode change;
2496 struct userData *uData;
2497 const char *errmsg;
2498
2499 mod_chanmode_init(&change);
2500 change.argc = 1;
2501 change.args[0].member = GetUserMode(channel, user);
2502 if(!change.args[0].member)
2503 {
2504 if(argc)
2505 reply("MSG_CHANNEL_ABSENT", channel->name);
2506 return 0;
2507 }
2508
2509 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2510 if(!uData)
2511 {
2512 if(argc)
2513 reply("CSMSG_GODMODE_UP", argv[0]);
2514 return 0;
2515 }
2516 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2517 {
2518 change.args[0].mode = MODE_CHANOP;
2519 errmsg = "CSMSG_ALREADY_OPPED";
2520 }
2521 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2522 {
2523 change.args[0].mode = MODE_VOICE;
2524 errmsg = "CSMSG_ALREADY_VOICED";
2525 }
2526 else
2527 {
2528 if(argc)
2529 reply("CSMSG_NO_ACCESS");
2530 return 0;
2531 }
2532 change.args[0].mode &= ~change.args[0].member->modes;
2533 if(!change.args[0].mode)
2534 {
2535 if(argc)
2536 reply(errmsg, channel->name);
2537 return 0;
2538 }
2539 modcmd_chanmode_announce(&change);
2540 return 1;
2541}
2542
2543static CHANSERV_FUNC(cmd_down)
2544{
2545 struct mod_chanmode change;
2546
2547 mod_chanmode_init(&change);
2548 change.argc = 1;
2549 change.args[0].member = GetUserMode(channel, user);
2550 if(!change.args[0].member)
2551 {
2552 if(argc)
2553 reply("MSG_CHANNEL_ABSENT", channel->name);
2554 return 0;
2555 }
2556
2557 if(!change.args[0].member->modes)
2558 {
2559 if(argc)
2560 reply("CSMSG_ALREADY_DOWN", channel->name);
2561 return 0;
2562 }
2563
2564 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2565 modcmd_chanmode_announce(&change);
2566 return 1;
2567}
2568
2569static 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)
2570{
2571 struct userData *cList;
2572
2573 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2574 {
2575 if(IsSuspended(cList->channel)
2576 || IsUserSuspended(cList)
2577 || !GetUserMode(cList->channel->channel, user))
2578 continue;
2579
2580 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2581 }
2582
2583 return 1;
2584}
2585
2586static CHANSERV_FUNC(cmd_upall)
2587{
2588 return cmd_all(CSFUNC_ARGS, cmd_up);
2589}
2590
2591static CHANSERV_FUNC(cmd_downall)
2592{
2593 return cmd_all(CSFUNC_ARGS, cmd_down);
2594}
2595
2596typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2597typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2598
2599static int
2600modify_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)
2601{
2602 unsigned int ii, valid;
2603 struct userNode *victim;
2604 struct mod_chanmode *change;
2605
2606 change = mod_chanmode_alloc(argc - 1);
2607
2608 for(ii=valid=0; ++ii < argc; )
2609 {
2610 if(!(victim = GetUserH(argv[ii])))
2611 continue;
2612 change->args[valid].mode = mode;
2613 change->args[valid].member = GetUserMode(channel, victim);
2614 if(!change->args[valid].member)
2615 continue;
2616 if(validate && !validate(user, channel, victim))
2617 continue;
2618 valid++;
2619 }
2620
2621 change->argc = valid;
2622 if(valid < (argc-1))
2623 reply("CSMSG_PROCESS_FAILED");
2624 if(valid)
2625 {
2626 modcmd_chanmode_announce(change);
2627 reply(action, channel->name);
2628 }
2629 mod_chanmode_free(change);
2630 return 1;
2631}
2632
2633static CHANSERV_FUNC(cmd_op)
2634{
2635 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2636}
2637
2638static CHANSERV_FUNC(cmd_deop)
2639{
2640 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2641}
2642
2643static CHANSERV_FUNC(cmd_voice)
2644{
2645 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2646}
2647
2648static CHANSERV_FUNC(cmd_devoice)
2649{
2650 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2651}
2652
2653static int
2654bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2655{
2656 unsigned int ii;
2657
2658 if(victimCount)
2659 *victimCount = 0;
2660 for(ii=0; ii<channel->members.used; ii++)
2661 {
2662 struct modeNode *mn = channel->members.list[ii];
2663
2664 if(IsService(mn->user))
2665 continue;
2666
2667 if(!user_matches_glob(mn->user, ban, 1))
2668 continue;
2669
2670 if(protect_user(mn->user, user, channel->channel_info))
2671 return 1;
2672
2673 if(victims)
2674 victims[(*victimCount)++] = mn;
2675 }
2676 return 0;
2677}
2678
2679static int
2680eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2681{
2682 struct userNode *victim;
2683 struct modeNode **victims;
2684 unsigned int offset, n, victimCount, duration = 0;
2685 char *reason = "Bye.", *ban, *name;
2686 char interval[INTERVALLEN];
2687
2688 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2689 REQUIRE_PARAMS(offset);
2690 if(argc > offset)
2691 {
2692 reason = unsplit_string(argv + offset, argc - offset, NULL);
2693 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2694 {
2695 /* Truncate the reason to a length of TOPICLEN, as
2696 the ircd does; however, leave room for an ellipsis
2697 and the kicker's nick. */
2698 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2699 }
2700 }
2701
2702 if((victim = GetUserH(argv[1])))
2703 {
2704 victims = alloca(sizeof(victims[0]));
2705 victims[0] = GetUserMode(channel, victim);
2706 /* XXX: The comparison with ACTION_KICK is just because all
2707 * other actions can work on users outside the channel, and we
2708 * want to allow those (e.g. unbans) in that case. If we add
2709 * some other ejection action for in-channel users, change
2710 * this too. */
2711 victimCount = victims[0] ? 1 : 0;
2712
2713 if(IsService(victim))
2714 {
2715 reply("MSG_SERVICE_IMMUNE", victim->nick);
2716 return 0;
2717 }
2718
2719 if((action == ACTION_KICK) && !victimCount)
2720 {
2721 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2722 return 0;
2723 }
2724
2725 if(protect_user(victim, user, channel->channel_info))
2726 {
2727 reply("CSMSG_USER_PROTECTED", victim->nick);
2728 return 0;
2729 }
2730
2731 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2732 name = victim->nick;
2733 }
2734 else
2735 {
2736 if(!is_ircmask(argv[1]))
2737 {
2738 reply("MSG_NICK_UNKNOWN", argv[1]);
2739 return 0;
2740 }
2741
2742 victims = alloca(sizeof(victims[0]) * channel->members.used);
2743
2744 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2745 {
2746 reply("CSMSG_MASK_PROTECTED", argv[1]);
2747 return 0;
2748 }
2749
2750 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2751 {
2752 reply("CSMSG_LAME_MASK", argv[1]);
2753 return 0;
2754 }
2755
2756 if((action == ACTION_KICK) && (victimCount == 0))
2757 {
2758 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2759 return 0;
2760 }
2761
2762 name = ban = strdup(argv[1]);
2763 }
2764
2765 /* Truncate the ban in place if necessary; we must ensure
2766 that 'ban' is a valid ban mask before sanitizing it. */
2767 sanitize_ircmask(ban);
2768
2769 if(action & ACTION_ADD_BAN)
2770 {
2771 struct banData *bData, *next;
2772
2773 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2774 {
2775 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2776 free(ban);
2777 return 0;
2778 }
2779
2780 if(action & ACTION_ADD_TIMED_BAN)
2781 {
2782 duration = ParseInterval(argv[2]);
2783
2784 if(duration < 15)
2785 {
2786 reply("CSMSG_DURATION_TOO_LOW");
2787 free(ban);
2788 return 0;
2789 }
2790 else if(duration > (86400 * 365 * 2))
2791 {
2792 reply("CSMSG_DURATION_TOO_HIGH");
2793 free(ban);
2794 return 0;
2795 }
2796 }
2797
2798 for(bData = channel->channel_info->bans; bData; bData = next)
2799 {
2800 if(match_ircglobs(bData->mask, ban))
2801 {
2802 int exact = !irccasecmp(bData->mask, ban);
2803
2804 /* The ban is redundant; there is already a ban
2805 with the same effect in place. */
2806 if(exact)
2807 {
2808 if(bData->reason)
2809 free(bData->reason);
2810 bData->reason = strdup(reason);
2811 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2812 if(cmd)
2813 reply("CSMSG_REASON_CHANGE", ban);
2814 if(!bData->expires)
2815 goto post_add_ban;
2816 }
2817 if(exact && bData->expires)
2818 {
2819 int reset = 0;
2820
2821 /* If the ban matches an existing one exactly,
2822 extend the expiration time if the provided
2823 duration is longer. */
2824 if(duration && ((time_t)(now + duration) > bData->expires))
2825 {
2826 bData->expires = now + duration;
2827 reset = 1;
2828 }
2829 else if(!duration)
2830 {
2831 bData->expires = 0;
2832 reset = 1;
2833 }
2834
2835 if(reset)
2836 {
2837 /* Delete the expiration timeq entry and
2838 requeue if necessary. */
2839 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2840
2841 if(bData->expires)
2842 timeq_add(bData->expires, expire_ban, bData);
2843
2844 if(!cmd)
2845 {
2846 /* automated kickban */
2847 }
2848 else if(duration)
2849 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2850 else
2851 reply("CSMSG_BAN_ADDED", name, channel->name);
2852
2853 goto post_add_ban;
2854 }
2855 }
2856 if(cmd)
2857 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2858
2859 free(ban);
2860 return 0;
2861 }
2862
2863 next = bData->next;
2864 if(match_ircglobs(ban, bData->mask))
2865 {
2866 /* The ban we are adding makes previously existing
2867 bans redundant; silently remove them. */
2868 del_channel_ban(bData);
2869 }
2870 }
2871
2872 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);
2873 free(ban);
2874 name = ban = strdup(bData->mask);
2875 }
2876 else if(ban)
2877 {
2878 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2879 {
2880 extern const char *hidden_host_suffix;
2881 const char *old_name = chanserv_conf.old_ban_names->list[n];
2882 char *new_mask;
2883 unsigned int l1, l2;
2884
2885 l1 = strlen(ban);
2886 l2 = strlen(old_name);
2887 if(l2+2 > l1)
2888 continue;
2889 if(irccasecmp(ban + l1 - l2, old_name))
2890 continue;
2891 new_mask = malloc(MAXLEN);
2892 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2893 free(ban);
2894 name = ban = new_mask;
2895 }
2896 }
2897
2898 post_add_ban:
2899 if(action & ACTION_BAN)
2900 {
2901 unsigned int exists;
2902 struct mod_chanmode *change;
2903
2904 if(channel->banlist.used >= MAXBANS)
2905 {
2906 if(cmd)
2907 reply("CSMSG_BANLIST_FULL", channel->name);
2908 free(ban);
2909 return 0;
2910 }
2911
2912 exists = ChannelBanExists(channel, ban);
2913 change = mod_chanmode_alloc(victimCount + 1);
2914 for(n = 0; n < victimCount; ++n)
2915 {
2916 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2917 change->args[n].member = victims[n];
2918 }
2919 if(!exists)
2920 {
2921 change->args[n].mode = MODE_BAN;
2922 change->args[n++].hostmask = ban;
2923 }
2924 change->argc = n;
2925 if(cmd)
2926 modcmd_chanmode_announce(change);
2927 else
2928 mod_chanmode_announce(chanserv, channel, change);
2929 mod_chanmode_free(change);
2930
2931 if(exists && (action == ACTION_BAN))
2932 {
2933 if(cmd)
2934 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2935 free(ban);
2936 return 0;
2937 }
2938 }
2939
2940 if(action & ACTION_KICK)
2941 {
2942 char kick_reason[MAXLEN];
2943 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2944
2945 for(n = 0; n < victimCount; n++)
2946 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2947 }
2948
2949 if(!cmd)
2950 {
2951 /* No response, since it was automated. */
2952 }
2953 else if(action & ACTION_ADD_BAN)
2954 {
2955 if(duration)
2956 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2957 else
2958 reply("CSMSG_BAN_ADDED", name, channel->name);
2959 }
2960 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2961 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2962 else if(action & ACTION_BAN)
2963 reply("CSMSG_BAN_DONE", name, channel->name);
2964 else if(action & ACTION_KICK && victimCount)
2965 reply("CSMSG_KICK_DONE", name, channel->name);
2966
2967 free(ban);
2968 return 1;
2969}
2970
2971static CHANSERV_FUNC(cmd_kickban)
2972{
2973 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2974}
2975
2976static CHANSERV_FUNC(cmd_kick)
2977{
2978 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2979}
2980
2981static CHANSERV_FUNC(cmd_ban)
2982{
2983 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2984}
2985
2986static CHANSERV_FUNC(cmd_addban)
2987{
2988 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2989}
2990
2991static CHANSERV_FUNC(cmd_addtimedban)
2992{
2993 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2994}
2995
2996static struct mod_chanmode *
2997find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2998{
2999 struct mod_chanmode *change;
3000 unsigned char *match;
3001 unsigned int ii, count;
3002
3003 match = alloca(bans->used);
3004 if(actee)
3005 {
3006 for(ii = count = 0; ii < bans->used; ++ii)
3007 {
3008 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3009 if(match[ii])
3010 count++;
3011 }
3012 }
3013 else
3014 {
3015 for(ii = count = 0; ii < bans->used; ++ii)
3016 {
3017 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3018 if(match[ii])
3019 count++;
3020 }
3021 }
3022 if(!count)
3023 return NULL;
3024 change = mod_chanmode_alloc(count);
3025 for(ii = count = 0; ii < bans->used; ++ii)
3026 {
3027 if(!match[ii])
3028 continue;
3029 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3030 change->args[count++].hostmask = bans->list[ii]->ban;
3031 }
3032 return change;
3033}
3034
3035static int
3036unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3037{
3038 struct userNode *actee;
3039 char *mask = NULL;
3040 int acted = 0;
3041
3042 REQUIRE_PARAMS(2);
3043
3044 /* may want to allow a comma delimited list of users... */
3045 if(!(actee = GetUserH(argv[1])))
3046 {
3047 if(!is_ircmask(argv[1]))
3048 {
3049 reply("MSG_NICK_UNKNOWN", argv[1]);
3050 return 0;
3051 }
3052
3053 mask = strdup(argv[1]);
3054 }
3055
3056 /* We don't sanitize the mask here because ircu
3057 doesn't do it. */
3058 if(action & ACTION_UNBAN)
3059 {
3060 struct mod_chanmode *change;
3061 change = find_matching_bans(&channel->banlist, actee, mask);
3062 if(change)
3063 {
3064 modcmd_chanmode_announce(change);
3065 mod_chanmode_free(change);
3066 acted = 1;
3067 }
3068 }
3069
3070 if(action & ACTION_DEL_BAN)
3071 {
3072 struct banData *ban, *next;
3073
3074 ban = channel->channel_info->bans;
3075 while(ban)
3076 {
3077 if(actee)
3078 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3079 ban = ban->next);
3080 else
3081 for( ; ban && !match_ircglobs(mask, ban->mask);
3082 ban = ban->next);
3083 if(!ban)
3084 break;
3085 next = ban->next;
3086 del_channel_ban(ban);
3087 ban = next;
3088 acted = 1;
3089 }
3090 }
3091
3092 if(!acted)
3093 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3094 else
3095 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3096 if(mask)
3097 free(mask);
3098 return 1;
3099}
3100
3101static CHANSERV_FUNC(cmd_unban)
3102{
3103 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3104}
3105
3106static CHANSERV_FUNC(cmd_delban)
3107{
3108 /* it doesn't necessarily have to remove the channel ban - may want
3109 to make that an option. */
3110 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3111}
3112
3113static CHANSERV_FUNC(cmd_unbanme)
3114{
3115 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3116 long flags = ACTION_UNBAN;
3117
3118 /* remove permanent bans if the user has the proper access. */
4048352e 3119 if(uData->access >= UL_MANAGER)
d76ed9a9 3120 flags |= ACTION_DEL_BAN;
3121
3122 argv[1] = user->nick;
3123 return unban_user(user, channel, 2, argv, cmd, flags);
3124}
3125
3126static CHANSERV_FUNC(cmd_unbanall)
3127{
3128 struct mod_chanmode *change;
3129 unsigned int ii;
3130
3131 if(!channel->banlist.used)
3132 {
3133 reply("CSMSG_NO_BANS", channel->name);
3134 return 0;
3135 }
3136
3137 change = mod_chanmode_alloc(channel->banlist.used);
3138 for(ii=0; ii<channel->banlist.used; ii++)
3139 {
3140 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3141 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3142 }
3143 modcmd_chanmode_announce(change);
3144 mod_chanmode_free(change);
3145 reply("CSMSG_BANS_REMOVED", channel->name);
3146 return 1;
3147}
3148
3149static CHANSERV_FUNC(cmd_open)
3150{
3151 struct mod_chanmode *change;
3152
3153 change = find_matching_bans(&channel->banlist, user, NULL);
3154 if(!change)
3155 change = mod_chanmode_alloc(0);
3156 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3157 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3158 && channel->channel_info->modes.modes_set)
3159 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3160 modcmd_chanmode_announce(change);
3161 reply("CSMSG_CHANNEL_OPENED", channel->name);
3162 mod_chanmode_free(change);
3163 return 1;
3164}
3165
3166static CHANSERV_FUNC(cmd_myaccess)
3167{
3168 static struct string_buffer sbuf;
3169 struct handle_info *target_handle;
3170 struct userData *uData;
3171
3172 if(argc < 2)
3173 target_handle = user->handle_info;
3174 else if(!IsHelping(user))
3175 {
3176 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3177 return 0;
3178 }
3179 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3180 return 0;
3181
3182 if(!target_handle->channels)
3183 {
3184 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3185 return 1;
3186 }
3187
3188 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3189 for(uData = target_handle->channels; uData; uData = uData->u_next)
3190 {
3191 struct chanData *cData = uData->channel;
3192
3193 if(uData->access > UL_OWNER)
3194 continue;
3195 if(IsProtected(cData)
3196 && (target_handle != user->handle_info)
3197 && !GetTrueChannelAccess(cData, user->handle_info))
3198 continue;
3199 sbuf.used = 0;
3200 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3201 if(uData->flags != USER_AUTO_OP)
3202 string_buffer_append(&sbuf, ',');
3203 if(IsUserSuspended(uData))
3204 string_buffer_append(&sbuf, 's');
3205 if(IsUserAutoOp(uData))
3206 {
3207 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3208 string_buffer_append(&sbuf, 'o');
3209 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3210 string_buffer_append(&sbuf, 'v');
3211 }
3212 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3213 string_buffer_append(&sbuf, 'i');
3214 if(uData->info)
3215 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3216 else
3217 string_buffer_append_string(&sbuf, ")]");
3218 string_buffer_append(&sbuf, '\0');
3219 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3220 }
3221
3222 return 1;
3223}
3224
3225static CHANSERV_FUNC(cmd_access)
3226{
3227 struct userNode *target;
3228 struct handle_info *target_handle;
3229 struct userData *uData;
3230 int helping;
3231 char prefix[MAXLEN];
3232
3233 if(argc < 2)
3234 {
3235 target = user;
3236 target_handle = target->handle_info;
3237 }
3238 else if((target = GetUserH(argv[1])))
3239 {
3240 target_handle = target->handle_info;
3241 }
3242 else if(argv[1][0] == '*')
3243 {
3244 if(!(target_handle = get_handle_info(argv[1]+1)))
3245 {
3246 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3247 return 0;
3248 }
3249 }
3250 else
3251 {
3252 reply("MSG_NICK_UNKNOWN", argv[1]);
3253 return 0;
3254 }
3255
3256 assert(target || target_handle);
3257
3258 if(target == chanserv)
3259 {
3260 reply("CSMSG_IS_CHANSERV");
3261 return 1;
3262 }
3263
3264 if(!target_handle)
3265 {
3266 if(IsOper(target))
3267 {
3268 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3269 return 0;
3270 }
3271 if(target != user)
3272 {
3273 reply("MSG_USER_AUTHENTICATE", target->nick);
3274 return 0;
3275 }
3276 reply("MSG_AUTHENTICATE");
3277 return 0;
3278 }
3279
3280 if(target)
3281 {
3282 const char *epithet = NULL, *type = NULL;
3283 if(IsOper(target))
3284 {
3285 epithet = chanserv_conf.irc_operator_epithet;
3286 type = "IRCOp";
3287 }
3288 else if(IsNetworkHelper(target))
3289 {
3290 epithet = chanserv_conf.network_helper_epithet;
3291 type = "network helper";
3292 }
3293 else if(IsSupportHelper(target))
3294 {
3295 epithet = chanserv_conf.support_helper_epithet;
3296 type = "support helper";
3297 }
3298 if(epithet)
3299 {
3300 if(target_handle->epithet)
3301 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3302 else if(epithet)
3303 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3304 }
3305 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3306 }
3307 else
3308 {
3309 sprintf(prefix, "%s", target_handle->handle);
3310 }
3311
3312 if(!channel->channel_info)
3313 {
3314 reply("CSMSG_NOT_REGISTERED", channel->name);
3315 return 1;
3316 }
3317
3318 helping = HANDLE_FLAGGED(target_handle, HELPING)
3319 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3320 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3321 {
3322 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3323 /* To prevent possible information leaks, only show infolines
3324 * if the requestor is in the channel or it's their own
3325 * handle. */
3326 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3327 {
3328 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3329 }
3330 /* Likewise, only say it's suspended if the user has active
3331 * access in that channel or it's their own entry. */
3332 if(IsUserSuspended(uData)
3333 && (GetChannelUser(channel->channel_info, user->handle_info)
3334 || (user->handle_info == uData->handle)))
3335 {
3336 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3337 }
3338 }
3339 else
3340 {
3341 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3342 }
3343
3344 return 1;
3345}
3346
3347static void
3348zoot_list(struct listData *list)
3349{
3350 struct userData *uData;
3351 unsigned int start, curr, highest, lowest;
3352 struct helpfile_table tmp_table;
3353 const char **temp, *msg;
3354
3355 if(list->table.length == 1)
3356 {
3357 if(list->search)
3358 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3359 else
3360 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3361 msg = user_find_message(list->user, "MSG_NONE");
3362 send_message_type(4, list->user, list->bot, " %s", msg);
3363 }
3364 tmp_table.width = list->table.width;
3365 tmp_table.flags = list->table.flags;
3366 list->table.contents[0][0] = " ";
3367 highest = list->highest;
3368 if(list->lowest != 0)
3369 lowest = list->lowest;
3370 else if(highest < 100)
3371 lowest = 1;
3372 else
3373 lowest = highest - 100;
3374 for(start = curr = 1; curr < list->table.length; )
3375 {
3376 uData = list->users[curr-1];
3377 list->table.contents[curr++][0] = " ";
3378 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3379 {
3380 if(list->search)
3381 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3382 else
3383 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3384 temp = list->table.contents[--start];
3385 list->table.contents[start] = list->table.contents[0];
3386 tmp_table.contents = list->table.contents + start;
3387 tmp_table.length = curr - start;
3388 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3389 list->table.contents[start] = temp;
3390 start = curr;
3391 highest = lowest - 1;
3392 lowest = (highest < 100) ? 0 : (highest - 99);
3393 }
3394 }
3395}
3396
3397static void
3398def_list(struct listData *list)
3399{
3400 const char *msg;
3401 if(list->search)
3402 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3403 else
3404 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3405 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3406 if(list->table.length == 1)
3407 {
3408 msg = user_find_message(list->user, "MSG_NONE");
3409 send_message_type(4, list->user, list->bot, " %s", msg);
3410 }
3411}
3412
3413static int
3414userData_access_comp(const void *arg_a, const void *arg_b)
3415{
3416 const struct userData *a = *(struct userData**)arg_a;
3417 const struct userData *b = *(struct userData**)arg_b;
3418 int res;
3419 if(a->access != b->access)
3420 res = b->access - a->access;
3421 else
3422 res = irccasecmp(a->handle->handle, b->handle->handle);
3423 return res;
3424}
3425
3426static int
3427cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3428{
3429 void (*send_list)(struct listData *);
3430 struct userData *uData;
3431 struct listData lData;
3432 unsigned int matches;
3433 const char **ary;
3434
3435 lData.user = user;
3436 lData.bot = cmd->parent->bot;
3437 lData.channel = channel;
3438 lData.lowest = lowest;
3439 lData.highest = highest;
3440 lData.search = (argc > 1) ? argv[1] : NULL;
3441 send_list = def_list;
3442 (void)zoot_list; /* since it doesn't show user levels */
3443
3444 if(user->handle_info)
3445 {
3446 switch(user->handle_info->userlist_style)
3447 {
3448 case HI_STYLE_DEF: send_list = def_list; break;
3449 case HI_STYLE_ZOOT: send_list = def_list; break;
3450 }
3451 }
3452
3453 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3454 matches = 0;
3455 for(uData = channel->channel_info->users; uData; uData = uData->next)
3456 {
3457 if((uData->access < lowest)
3458 || (uData->access > highest)
3459 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3460 continue;
3461 lData.users[matches++] = uData;
3462 }
3463 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3464
3465 lData.table.length = matches+1;
3466 lData.table.width = 4;
3467 lData.table.flags = TABLE_NO_FREE;
3468 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3469 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3470 lData.table.contents[0] = ary;
3471 ary[0] = "Access";
3472 ary[1] = "Account";
3473 ary[2] = "Last Seen";
3474 ary[3] = "Status";
3475 for(matches = 1; matches < lData.table.length; ++matches)
3476 {
3477 struct userData *uData = lData.users[matches-1];
3478 char seen[INTERVALLEN];
3479
3480 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3481 lData.table.contents[matches] = ary;
3482 ary[0] = strtab(uData->access);
3483 ary[1] = uData->handle->handle;
3484 if(uData->present)
3485 ary[2] = "Here";
3486 else if(!uData->seen)
3487 ary[2] = "Never";
3488 else
3489 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3490 ary[2] = strdup(ary[2]);
3491 if(IsUserSuspended(uData))
3492 ary[3] = "Suspended";
3493 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3494 ary[3] = "Vacation";
3495 else
3496 ary[3] = "Normal";
3497 }
3498 send_list(&lData);
3499 for(matches = 1; matches < lData.table.length; ++matches)
3500 {
3501 free((char*)lData.table.contents[matches][2]);
3502 free(lData.table.contents[matches]);
3503 }
3504 free(lData.table.contents[0]);
3505 free(lData.table.contents);
3506 return 1;
3507}
3508
3509static CHANSERV_FUNC(cmd_users)
3510{
3511 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3512}
3513
3514static CHANSERV_FUNC(cmd_wlist)
3515{
3516 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3517}
3518
3519static CHANSERV_FUNC(cmd_clist)
3520{
3521 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3522}
3523
3524static CHANSERV_FUNC(cmd_mlist)
3525{
4048352e 3526 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
d76ed9a9 3527}
3528
3529static CHANSERV_FUNC(cmd_olist)
3530{
4048352e 3531 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
d76ed9a9 3532}
3533
3534static CHANSERV_FUNC(cmd_plist)
3535{
3536 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3537}
3538
3539static CHANSERV_FUNC(cmd_bans)
3540{
3541 struct helpfile_table tbl;
3542 unsigned int matches = 0, timed = 0, ii;
3543 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3544 const char *msg_never, *triggered, *expires;
3545 struct banData *ban, **bans;
3546
3547 if(argc > 1)
3548 search = argv[1];
3549 else
3550 search = NULL;
3551
3552 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3553
3554 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3555 {
3556 if(search && !match_ircglobs(search, ban->mask))
3557 continue;
3558 bans[matches++] = ban;
3559 if(ban->expires)
3560 timed = 1;
3561 }
3562
3563 tbl.length = matches + 1;
3564 tbl.width = 4 + timed;
3565 tbl.flags = 0;
3566 tbl.flags = TABLE_NO_FREE;
3567 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3568 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3569 tbl.contents[0][0] = "Mask";
3570 tbl.contents[0][1] = "Set By";
3571 tbl.contents[0][2] = "Triggered";
3572 if(timed)
3573 {
3574 tbl.contents[0][3] = "Expires";
3575 tbl.contents[0][4] = "Reason";
3576 }
3577 else
3578 tbl.contents[0][3] = "Reason";
3579 if(!matches)
3580 {
3581 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3582 reply("MSG_NONE");
3583 free(tbl.contents[0]);
3584 free(tbl.contents);
3585 return 0;
3586 }
3587
3588 msg_never = user_find_message(user, "MSG_NEVER");
3589 for(ii = 0; ii < matches; )
3590 {
3591 ban = bans[ii];
3592
3593 if(!timed)
3594 expires = "";
3595 else if(ban->expires)
3596 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3597 else
3598 expires = msg_never;
3599
3600 if(ban->triggered)
3601 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3602 else
3603 triggered = msg_never;
3604
3605 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3606 tbl.contents[ii][0] = ban->mask;
3607 tbl.contents[ii][1] = ban->owner;
3608 tbl.contents[ii][2] = strdup(triggered);
3609 if(timed)
3610 {
3611 tbl.contents[ii][3] = strdup(expires);
3612 tbl.contents[ii][4] = ban->reason;
3613 }
3614 else
3615 tbl.contents[ii][3] = ban->reason;
3616 }
3617 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3618 reply("MSG_MATCH_COUNT", matches);
3619 for(ii = 1; ii < tbl.length; ++ii)
3620 {
3621 free((char*)tbl.contents[ii][2]);
3622 if(timed)
3623 free((char*)tbl.contents[ii][3]);
3624 free(tbl.contents[ii]);
3625 }
3626 free(tbl.contents[0]);
3627 free(tbl.contents);
3628 return 1;
3629}
3630
3631static int
3632bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3633{
3634 struct chanData *cData = channel->channel_info;
3635 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3636 return 0;
3637 if(cData->topic_mask)
3638 return !match_ircglob(new_topic, cData->topic_mask);
3639 else if(cData->topic)
3640 return irccasecmp(new_topic, cData->topic);
3641 else
3642 return 0;
3643}
3644
3645static CHANSERV_FUNC(cmd_topic)
3646{
3647 struct chanData *cData;
3648 char *topic;
3649
3650 cData = channel->channel_info;
3651 if(argc < 2)
3652 {
3653 if(cData->topic)
3654 {
3655 SetChannelTopic(channel, chanserv, cData->topic, 1);
3656 reply("CSMSG_TOPIC_SET", cData->topic);
3657 return 1;
3658 }
3659
3660 reply("CSMSG_NO_TOPIC", channel->name);
3661 return 0;
3662 }
3663
3664 topic = unsplit_string(argv + 1, argc - 1, NULL);
3665 /* If they say "!topic *", use an empty topic. */
3666 if((topic[0] == '*') && (topic[1] == 0))
3667 topic[0] = 0;
3668 if(bad_topic(channel, user, topic))
3669 {
3670 char *topic_mask = cData->topic_mask;
3671 if(topic_mask)
3672 {
3673 char new_topic[TOPICLEN+1], tchar;
3674 int pos=0, starpos=-1, dpos=0, len;
3675
3676 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3677 {
3678 switch(tchar)
3679 {
3680 case '*':
3681 if(starpos != -1)
3682 goto bad_mask;
3683 len = strlen(topic);
3684 if((dpos + len) > TOPICLEN)
3685 len = TOPICLEN + 1 - dpos;
3686 memcpy(new_topic+dpos, topic, len);
3687 dpos += len;
3688 starpos = pos;
3689 break;
3690 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3691 default: new_topic[dpos++] = tchar; break;
3692 }
3693 }
3694 if((dpos > TOPICLEN) || tchar)
3695 {
3696 bad_mask:
3697 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3698 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3699 return 0;
3700 }
3701 new_topic[dpos] = 0;
3702 SetChannelTopic(channel, chanserv, new_topic, 1);
3703 } else {
3704 reply("CSMSG_TOPIC_LOCKED", channel->name);
3705 return 0;
3706 }
3707 }
3708 else
3709 SetChannelTopic(channel, chanserv, topic, 1);
3710
3711 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3712 {
3713 /* Grab the topic and save it as the default topic. */
3714 free(cData->topic);
3715 cData->topic = strdup(channel->topic);
3716 }
3717
3718 return 1;
3719}
3720
3721static CHANSERV_FUNC(cmd_mode)
3722{
3723 struct mod_chanmode *change;
3724
3725 if(argc < 2)
3726 {
3727 change = &channel->channel_info->modes;
3728 if(change->modes_set || change->modes_clear) {
3729 modcmd_chanmode_announce(change);
3730 reply("CSMSG_DEFAULTED_MODES", channel->name);
3731 } else
3732 reply("CSMSG_NO_MODES", channel->name);
3733 return 1;
3734 }
3735
3736 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3737 if(!change)
3738 {
3739 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3740 return 0;
3741 }
3742
3743 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3744 && mode_lock_violated(&channel->channel_info->modes, change))
3745 {
3746 char modes[MAXLEN];
3747 mod_chanmode_format(&channel->channel_info->modes, modes);
3748 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3749 return 0;
3750 }
3751
3752 modcmd_chanmode_announce(change);
3753 mod_chanmode_free(change);
3754 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3755 return 1;
3756}
3757
3758static CHANSERV_FUNC(cmd_invite)
3759{
3760 struct userData *uData;
3761 struct userNode *invite;
3762
3763 uData = GetChannelUser(channel->channel_info, user->handle_info);
3764
3765 if(argc > 1)
3766 {
3767 if(!(invite = GetUserH(argv[1])))
3768 {
3769 reply("MSG_NICK_UNKNOWN", argv[1]);
3770 return 0;
3771 }
3772 }
3773 else
3774 invite = user;
3775
3776 if(GetUserMode(channel, invite))
3777 {
3778 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3779 return 0;
3780 }
3781
3782 if(user != invite)
3783 {
3784 if(argc > 2)
3785 {
3786 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3787 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3788 }
3789 else
3790 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3791 }
3792 irc_invite(chanserv, invite, channel);
3793 if(argc > 1)
3794 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3795
3796 return 1;
3797}
3798
3799static CHANSERV_FUNC(cmd_inviteme)
3800{
3801 if(GetUserMode(channel, user))
3802 {
3803 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3804 return 0;
3805 }
3806 if(channel->channel_info
3807 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3808 {
3809 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3810 return 0;
3811 }
3812 irc_invite(cmd->parent->bot, user, channel);
3813 return 1;
3814}
3815
3816static void
3817show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3818{
3819 unsigned int combo;
3820 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3821
3822 /* We display things based on two dimensions:
3823 * - Issue time: present or absent
3824 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3825 * (in order of precedence, so something both expired and revoked
3826 * only counts as revoked)
3827 */
3828 combo = (suspended->issued ? 4 : 0)
3829 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3830 switch(combo) {
3831 case 0: /* no issue time, indefinite expiration */
3832 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3833 break;
3834 case 1: /* no issue time, expires in future */
3835 intervalString(buf1, suspended->expires-now, user->handle_info);
3836 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3837 break;
3838 case 2: /* no issue time, expired */
3839 intervalString(buf1, now-suspended->expires, user->handle_info);
3840 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3841 break;
3842 case 3: /* no issue time, revoked */
3843 intervalString(buf1, now-suspended->revoked, user->handle_info);
3844 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3845 break;
3846 case 4: /* issue time set, indefinite expiration */
3847 intervalString(buf1, now-suspended->issued, user->handle_info);
3848 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3849 break;
3850 case 5: /* issue time set, expires in future */
3851 intervalString(buf1, now-suspended->issued, user->handle_info);
3852 intervalString(buf2, suspended->expires-now, user->handle_info);
3853 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3854 break;
3855 case 6: /* issue time set, expired */
3856 intervalString(buf1, now-suspended->issued, user->handle_info);
3857 intervalString(buf2, now-suspended->expires, user->handle_info);
3858 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3859 break;
3860 case 7: /* issue time set, revoked */
3861 intervalString(buf1, now-suspended->issued, user->handle_info);
3862 intervalString(buf2, now-suspended->revoked, user->handle_info);
3863 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3864 break;
3865 default:
3866 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3867 return;
3868 }
3869}
3870
3871static CHANSERV_FUNC(cmd_info)
3872{
3873 char modes[MAXLEN], buffer[INTERVALLEN];
3874 struct userData *uData, *owner;
3875 struct chanData *cData;
3876 struct do_not_register *dnr;
3877 struct note *note;
3878 dict_iterator_t it;
3879 int privileged;
3880
3881 cData = channel->channel_info;
3882 reply("CSMSG_CHANNEL_INFO", channel->name);
3883
3884 uData = GetChannelUser(cData, user->handle_info);
3885 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3886 {
3887 mod_chanmode_format(&cData->modes, modes);
3888 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3889 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3890 }
3891
3892 for(it = dict_first(cData->notes); it; it = iter_next(it))
3893 {
3894 int padding;
3895
3896 note = iter_data(it);
3897 if(!note_type_visible_to_user(cData, note->type, user))
3898 continue;
3899
3900 padding = PADLEN - 1 - strlen(iter_key(it));
3901 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3902 }
3903
3904 reply("CSMSG_CHANNEL_MAX", cData->max);
3905 for(owner = cData->users; owner; owner = owner->next)
3906 if(owner->access == UL_OWNER)
3907 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3908 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3909 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3910 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3911 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3912
3913 privileged = IsStaff(user);
3914 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3915 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3916
3917 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3918 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3919
3920 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3921 {
3922 struct suspended *suspended;
3923 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3924 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3925 show_suspension_info(cmd, user, suspended);
3926 }
3927 else if(IsSuspended(cData))
3928 {
3929 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3930 show_suspension_info(cmd, user, cData->suspended);
3931 }
3932 return 1;
3933}
3934
3935static CHANSERV_FUNC(cmd_netinfo)
3936{
3937 extern time_t boot_time;
3938 extern unsigned long burst_length;
3939 char interval[INTERVALLEN];
3940
3941 reply("CSMSG_NETWORK_INFO");
3942 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3943 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3944 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3945 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3946 reply("CSMSG_NETWORK_BANS", banCount);
3947 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3948 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3949 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3950 return 1;
3951}
3952
3953static void
3954send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3955{
3956 struct helpfile_table table;
3957 unsigned int nn;
3958 struct userNode *user;
3959 char *nick;
3960
3961 table.length = 0;
3962 table.width = 1;
3963 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3964 table.contents = alloca(list->used*sizeof(*table.contents));
3965 for(nn=0; nn<list->used; nn++)
3966 {
3967 user = list->list[nn];
3968 if(user->modes & skip_flags)
3969 continue;
3970 if(IsBot(user))
3971 continue;
3972 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3973 if(IsAway(user))
3974 {
3975 nick = alloca(strlen(user->nick)+3);
3976 sprintf(nick, "(%s)", user->nick);
3977 }
3978 else
3979 nick = user->nick;
3980 table.contents[table.length][0] = nick;
3981 table.length++;
3982 }
3983 table_send(chanserv, to->nick, 0, NULL, table);
3984}
3985
3986static CHANSERV_FUNC(cmd_ircops)
3987{
3988 reply("CSMSG_STAFF_OPERS");
3989 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3990 return 1;
3991}
3992
3993static CHANSERV_FUNC(cmd_helpers)
3994{
3995 reply("CSMSG_STAFF_HELPERS");
3996 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3997 return 1;
3998}
3999
4000static CHANSERV_FUNC(cmd_staff)
4001{
4002 reply("CSMSG_NETWORK_STAFF");
4003 cmd_ircops(CSFUNC_ARGS);
4004 cmd_helpers(CSFUNC_ARGS);
4005 return 1;
4006}
4007
4008static CHANSERV_FUNC(cmd_peek)
4009{
4010 struct modeNode *mn;
4011 char modes[MODELEN];
4012 unsigned int n;
4013 struct helpfile_table table;
4014
4015 irc_make_chanmode(channel, modes);
4016
4017 reply("CSMSG_PEEK_INFO", channel->name);
4018 reply("CSMSG_PEEK_TOPIC", channel->topic);
4019 reply("CSMSG_PEEK_MODES", modes);
4020 reply("CSMSG_PEEK_USERS", channel->members.used);
4021
4022 table.length = 0;
4023 table.width = 1;
4024 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4025 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4026 for(n = 0; n < channel->members.used; n++)
4027 {
4028 mn = channel->members.list[n];
4029 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4030 continue;
4031 table.contents[table.length] = alloca(sizeof(**table.contents));
4032 table.contents[table.length][0] = mn->user->nick;
4033 table.length++;
4034 }
4035 if(table.length)
4036 {
4037 reply("CSMSG_PEEK_OPS");
4038 table_send(chanserv, user->nick, 0, NULL, table);
4039 }
4040 else
4041 reply("CSMSG_PEEK_NO_OPS");
4042 return 1;
4043}
4044
4045static MODCMD_FUNC(cmd_wipeinfo)
4046{
4047 struct handle_info *victim;
4048 struct userData *ud, *actor;
4049
4050 REQUIRE_PARAMS(2);
4051 actor = GetChannelUser(channel->channel_info, user->handle_info);
4052 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4053 return 0;
4054 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4055 {
4056 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4057 return 0;
4058 }
4059 if((ud->access >= actor->access) && (ud != actor))
4060 {
4061 reply("MSG_USER_OUTRANKED", victim->handle);
4062 return 0;
4063 }
4064 if(ud->info)
4065 free(ud->info);
4066 ud->info = NULL;
4067 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4068 return 1;
4069}
4070
4071static CHANSERV_FUNC(cmd_resync)
4072{
4073 struct mod_chanmode *changes;
4074 struct chanData *cData = channel->channel_info;
4075 unsigned int ii, used;
4076
4077 changes = mod_chanmode_alloc(channel->members.used * 2);
4078 for(ii = used = 0; ii < channel->members.used; ++ii)
4079 {
4080 struct modeNode *mn = channel->members.list[ii];
4081 struct userData *uData;
4082
4083 if(IsService(mn->user))
4084 continue;
4085
4086 uData = GetChannelAccess(cData, mn->user->handle_info);
4087 if(!cData->lvlOpts[lvlGiveOps]
4088 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4089 {
4090 if(!(mn->modes & MODE_CHANOP))
4091 {
4092 changes->args[used].mode = MODE_CHANOP;
4093 changes->args[used++].member = mn;
4094 }
4095 }
4096 else if(!cData->lvlOpts[lvlGiveVoice]
4097 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4098 {
4099 if(mn->modes & MODE_CHANOP)
4100 {
4101 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4102 changes->args[used++].member = mn;
4103 }
4104 if(!(mn->modes & MODE_VOICE))
4105 {
4106 changes->args[used].mode = MODE_VOICE;
4107 changes->args[used++].member = mn;
4108 }
4109 }
4110 else
4111 {
4112 if(mn->modes)
4113 {
4114 changes->args[used].mode = MODE_REMOVE | mn->modes;
4115 changes->args[used++].member = mn;
4116 }
4117 }
4118 }
4119 changes->argc = used;
4120 modcmd_chanmode_announce(changes);
4121 mod_chanmode_free(changes);
4122 reply("CSMSG_RESYNCED_USERS", channel->name);
4123 return 1;
4124}
4125
4126static CHANSERV_FUNC(cmd_seen)
4127{
4128 struct userData *uData;
4129 struct handle_info *handle;
4130 char seen[INTERVALLEN];
4131
4132 REQUIRE_PARAMS(2);
4133
4134 if(!irccasecmp(argv[1], chanserv->nick))
4135 {
4136 reply("CSMSG_IS_CHANSERV");
4137 return 1;
4138 }
4139
4140 if(!(handle = get_handle_info(argv[1])))
4141 {
4142 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4143 return 0;
4144 }
4145
4146 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4147 {
4148 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4149 return 0;
4150 }
4151
4152 if(uData->present)
4153 reply("CSMSG_USER_PRESENT", handle->handle);
4154 else if(uData->seen)
4155 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4156 else
4157 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4158
4159 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4160 reply("CSMSG_USER_VACATION", handle->handle);
4161
4162 return 1;
4163}
4164
4165static MODCMD_FUNC(cmd_names)
4166{
4167 struct userNode *targ;
4168 struct userData *targData;
4169 unsigned int ii, pos;
4170 char buf[400];
4171
4172 for(ii=pos=0; ii<channel->members.used; ++ii)
4173 {
4174 targ = channel->members.list[ii]->user;
4175 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4176 if(!targData)
4177 continue;
4178 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4179 {
4180 buf[pos] = 0;
4181 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4182 pos = 0;
4183 }
4184 buf[pos++] = ' ';
4185 if(IsUserSuspended(targData))
4186 buf[pos++] = 's';
4187 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4188 }
4189 buf[pos] = 0;
4190 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4191 reply("CSMSG_END_NAMES", channel->name);
4192 return 1;
4193}
4194
4195static int
4196note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4197{
4198 switch(ntype->visible_type)
4199 {
4200 case NOTE_VIS_ALL: return 1;
4201 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4202 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4203 }
4204}
4205
4206static int
4207note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4208{
4209 struct userData *uData;
4210
4211 switch(ntype->set_access_type)
4212 {
4213 case NOTE_SET_CHANNEL_ACCESS:
4214 if(!user->handle_info)
4215 return 0;
4216 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4217 return 0;
4218 return uData->access >= ntype->set_access.min_ulevel;
4219 case NOTE_SET_CHANNEL_SETTER:
4220 return check_user_level(channel, user, lvlSetters, 1, 0);
4221 case NOTE_SET_PRIVILEGED: default:
4222 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4223 }
4224}
4225
4226static CHANSERV_FUNC(cmd_note)
4227{
4228 struct chanData *cData;
4229 struct note *note;
4230 struct note_type *ntype;
4231
4232 cData = channel->channel_info;
4233 if(!cData)
4234 {
4235 reply("CSMSG_NOT_REGISTERED", channel->name);
4236 return 0;
4237 }
4238
4239 /* If no arguments, show all visible notes for the channel. */
4240 if(argc < 2)
4241 {
4242 dict_iterator_t it;
4243 unsigned int count;
4244
4245 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4246 {
4247 note = iter_data(it);
4248 if(!note_type_visible_to_user(cData, note->type, user))
4249 continue;
4250 if(!count++)
4251 reply("CSMSG_NOTELIST_HEADER", channel->name);
4252 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4253 }
4254 if(count)
4255 reply("CSMSG_NOTELIST_END", channel->name);
4256 else
4257 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4258 }
4259 /* If one argument, show the named note. */
4260 else if(argc == 2)
4261 {
4262 if((note = dict_find(cData->notes, argv[1], NULL))
4263 && note_type_visible_to_user(cData, note->type, user))
4264 {
4265 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4266 }
4267 else if((ntype = dict_find(note_types, argv[1], NULL))
4268 && note_type_visible_to_user(NULL, ntype, user))
4269 {
4270 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4271 return 0;
4272 }
4273 else
4274 {
4275 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4276 return 0;
4277 }
4278 }
4279 /* Assume they're trying to set a note. */
4280 else
4281 {
4282 char *note_text;
4283 ntype = dict_find(note_types, argv[1], NULL);
4284 if(!ntype)
4285 {
4286 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4287 return 0;
4288 }
4289 else if(note_type_settable_by_user(channel, ntype, user))
4290 {
4291 note_text = unsplit_string(argv+2, argc-2, NULL);
4292 if((note = dict_find(cData->notes, argv[1], NULL)))
4293 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4294 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4295 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4296
4297 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4298 {
4299 /* The note is viewable to staff only, so return 0
4300 to keep the invocation from getting logged (or
4301 regular users can see it in !events). */
4302 return 0;
4303 }
4304 }
4305 else
4306 {
4307 reply("CSMSG_NO_ACCESS");
4308 return 0;
4309 }
4310 }
4311 return 1;
4312}
4313
4314static CHANSERV_FUNC(cmd_delnote)
4315{
4316 struct note *note;
4317
4318 REQUIRE_PARAMS(2);
4319 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4320 || !note_type_settable_by_user(channel, note->type, user))
4321 {
4322 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4323 return 0;
4324 }
4325 dict_remove(channel->channel_info->notes, note->type->name);
4326 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4327 return 1;
4328}
4329
4330static CHANSERV_FUNC(cmd_events)
4331{
4332 struct logSearch discrim;
4333 struct logReport report;
4334 unsigned int matches, limit;
4335
4336 limit = (argc > 1) ? atoi(argv[1]) : 10;
4337 if(limit < 1 || limit > 200)
4338 limit = 10;
4339
4340 memset(&discrim, 0, sizeof(discrim));
4341 discrim.masks.bot = chanserv;
4342 discrim.masks.channel_name = channel->name;
4343 if(argc > 2)
4344 discrim.masks.command = argv[2];
4345 discrim.limit = limit;
4346 discrim.max_time = INT_MAX;
4347 discrim.severities = 1 << LOG_COMMAND;
4348 report.reporter = chanserv;
4349 report.user = user;
4350 reply("CSMSG_EVENT_SEARCH_RESULTS");
4351 matches = log_entry_search(&discrim, log_report_entry, &report);
4352 if(matches)
4353 reply("MSG_MATCH_COUNT", matches);
4354 else
4355 reply("MSG_NO_MATCHES");
4356 return 1;
4357}
4358
4359static CHANSERV_FUNC(cmd_say)
4360{
4361 char *msg;
4362 if(channel)
4363 {
4364 REQUIRE_PARAMS(2);
4365 msg = unsplit_string(argv + 1, argc - 1, NULL);
4366 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4367 }
4368 else if(GetUserH(argv[1]))
4369 {
4370 REQUIRE_PARAMS(3);
4371 msg = unsplit_string(argv + 2, argc - 2, NULL);
4372 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4373 }
4374 else
4375 {
4376 reply("MSG_NOT_TARGET_NAME");
4377 return 0;
4378 }
4379 return 1;
4380}
4381
4382static CHANSERV_FUNC(cmd_emote)
4383{
4384 char *msg;
4385 assert(argc >= 2);
4386 if(channel)
4387 {
4388 /* CTCP is so annoying. */
4389 msg = unsplit_string(argv + 1, argc - 1, NULL);
4390 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4391 }
4392 else if(GetUserH(argv[1]))
4393 {
4394 msg = unsplit_string(argv + 2, argc - 2, NULL);
4395 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4396 }
4397 else
4398 {
4399 reply("MSG_NOT_TARGET_NAME");
4400 return 0;
4401 }
4402 return 1;
4403}
4404
4405struct channelList *
4406chanserv_support_channels(void)
4407{
4408 return &chanserv_conf.support_channels;
4409}
4410
4411static CHANSERV_FUNC(cmd_expire)
4412{
4413 int channel_count = registered_channels;
4414 expire_channels(NULL);
4415 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4416 return 1;
4417}
4418
4419static void
4420chanserv_expire_suspension(void *data)
4421{
4422 struct suspended *suspended = data;
4423 struct chanNode *channel;
4424 struct mod_chanmode change;
4425
4426 if(!suspended->expires || (now < suspended->expires))
4427 suspended->revoked = now;
4428 channel = suspended->cData->channel;
4429 suspended->cData->channel = channel;
4430 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4431 mod_chanmode_init(&change);
4432 change.argc = 1;
4433 change.args[0].mode = MODE_CHANOP;
4434 change.args[0].member = AddChannelUser(chanserv, channel);
4435 mod_chanmode_announce(chanserv, channel, &change);
4436}
4437
4438static CHANSERV_FUNC(cmd_csuspend)
4439{
4440 struct suspended *suspended;
4441 char reason[MAXLEN];
4442 time_t expiry, duration;
4443 struct userData *uData;
4444
4445 REQUIRE_PARAMS(3);
4446
4447 if(IsProtected(channel->channel_info))
4448 {
4449 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4450 return 0;
4451 }
4452
4453 if(argv[1][0] == '!')
4454 argv[1]++;
4455 else if(IsSuspended(channel->channel_info))
4456 {
4457 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4458 show_suspension_info(cmd, user, channel->channel_info->suspended);
4459 return 0;
4460 }
4461
4462 if(!strcmp(argv[1], "0"))
4463 expiry = 0;
4464 else if((duration = ParseInterval(argv[1])))
4465 expiry = now + duration;
4466 else
4467 {
4468 reply("MSG_INVALID_DURATION", argv[1]);
4469 return 0;
4470 }
4471
4472 unsplit_string(argv + 2, argc - 2, reason);
4473
4474 suspended = calloc(1, sizeof(*suspended));
4475 suspended->revoked = 0;
4476 suspended->issued = now;
4477 suspended->suspender = strdup(user->handle_info->handle);
4478 suspended->expires = expiry;
4479 suspended->reason = strdup(reason);
4480 suspended->cData = channel->channel_info;
4481 suspended->previous = suspended->cData->suspended;
4482 suspended->cData->suspended = suspended;
4483
4484 if(suspended->expires)
4485 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4486
4487 if(IsSuspended(channel->channel_info))
4488 {
4489 suspended->previous->revoked = now;
4490 if(suspended->previous->expires)
4491 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4492 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4493 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4494 }
4495 else
4496 {
4497 /* Mark all users in channel as absent. */
4498 for(uData = channel->channel_info->users; uData; uData = uData->next)
4499 {
4500 if(uData->present)
4501 {
4502 uData->seen = now;
4503 uData->present = 0;
4504 }
4505 }
4506
4507 /* Mark the channel as suspended, then part. */
4508 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4509 DelChannelUser(chanserv, channel, suspended->reason, 0);
4510 reply("CSMSG_SUSPENDED", channel->name);
4511 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4512 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4513 }
4514 return 1;
4515}
4516
4517static CHANSERV_FUNC(cmd_cunsuspend)
4518{
4519 struct suspended *suspended;
4520 char message[MAXLEN];
4521
4522 if(!IsSuspended(channel->channel_info))
4523 {
4524 reply("CSMSG_NOT_SUSPENDED", channel->name);
4525 return 0;
4526 }
4527
4528 suspended = channel->channel_info->suspended;
4529
4530 /* Expire the suspension and join ChanServ to the channel. */
4531 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4532 chanserv_expire_suspension(suspended);
4533 reply("CSMSG_UNSUSPENDED", channel->name);
4534 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4535 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4536 return 1;
4537}
4538
4539typedef struct chanservSearch
4540{
4541 char *name;
4542 char *registrar;
4543
4544 time_t unvisited;
4545 time_t registered;
4546
4547 unsigned long flags;
4548 unsigned int limit;
4549} *search_t;
4550
4551typedef void (*channel_search_func)(struct chanData *channel, void *data);
4552
4553static search_t
4554chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4555{
4556 search_t search;
4557 unsigned int i;
4558
4559 search = malloc(sizeof(struct chanservSearch));
4560 memset(search, 0, sizeof(*search));
4561 search->limit = 25;
4562
4563 for(i = 0; i < argc; i++)
4564 {
4565 /* Assume all criteria require arguments. */
4566 if(i == (argc - 1))
4567 {
4568 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4569 goto fail;
4570 }
4571
4572 if(!irccasecmp(argv[i], "name"))
4573 search->name = argv[++i];
4574 else if(!irccasecmp(argv[i], "registrar"))
4575 search->registrar = argv[++i];
4576 else if(!irccasecmp(argv[i], "unvisited"))
4577 search->unvisited = ParseInterval(argv[++i]);
4578 else if(!irccasecmp(argv[i], "registered"))
4579 search->registered = ParseInterval(argv[++i]);
4580 else if(!irccasecmp(argv[i], "flags"))
4581 {
4582 i++;
4583 if(!irccasecmp(argv[i], "nodelete"))
4584 search->flags |= CHANNEL_NODELETE;
4585 else if(!irccasecmp(argv[i], "suspended"))
4586 search->flags |= CHANNEL_SUSPENDED;
4587 else
4588 {
4589 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4590 goto fail;
4591 }
4592 }
4593 else if(!irccasecmp(argv[i], "limit"))
4594 search->limit = strtoul(argv[++i], NULL, 10);
4595 else
4596 {
4597 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4598 goto fail;
4599 }
4600 }
4601
4602 if(search->name && !strcmp(search->name, "*"))
4603 search->name = 0;
4604 if(search->registrar && !strcmp(search->registrar, "*"))
4605 search->registrar = 0;
4606
4607 return search;
4608 fail:
4609 free(search);
4610 return NULL;
4611}
4612
4613static int
4614chanserv_channel_match(struct chanData *channel, search_t search)
4615{
4616 const char *name = channel->channel->name;
4617 if((search->name && !match_ircglob(name, search->name)) ||
4618 (search->registrar && !channel->registrar) ||
4619 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4620 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4621 (search->registered && (now - channel->registered) > search->registered) ||
4622 (search->flags && ((search->flags & channel->flags) != search->flags)))
4623 return 0;
4624
4625 return 1;
4626}
4627
4628static unsigned int
4629chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4630{
4631 struct chanData *channel;
4632 unsigned int matches = 0;
4633
4634 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4635 {
4636 if(!chanserv_channel_match(channel, search))
4637 continue;
4638 matches++;
4639 smf(channel, data);
4640 }
4641
4642 return matches;
4643}
4644
4645static void
4646search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4647{
4648}
4649
4650static void
4651search_print(struct chanData *channel, void *data)
4652{
4653 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4654}
4655
4656static CHANSERV_FUNC(cmd_search)
4657{
4658 search_t search;
4659 unsigned int matches;
4660 channel_search_func action;
4661
4662 REQUIRE_PARAMS(3);
4663
4664 if(!irccasecmp(argv[1], "count"))
4665 action = search_count;
4666 else if(!irccasecmp(argv[1], "print"))
4667 action = search_print;
4668 else
4669 {
4670 reply("CSMSG_ACTION_INVALID", argv[1]);
4671 return 0;
4672 }
4673
4674 search = chanserv_search_create(user, argc - 2, argv + 2);
4675 if(!search)
4676 return 0;
4677
4678 if(action == search_count)
4679 search->limit = INT_MAX;
4680
4681 if(action == search_print)
4682 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4683
4684 matches = chanserv_channel_search(search, action, user);
4685
4686 if(matches)
4687 reply("MSG_MATCH_COUNT", matches);
4688 else
4689 reply("MSG_NO_MATCHES");
4690
4691 free(search);
4692 return 1;
4693}
4694
4695static CHANSERV_FUNC(cmd_unvisited)
4696{
4697 struct chanData *cData;
4698 time_t interval = chanserv_conf.channel_expire_delay;
4699 char buffer[INTERVALLEN];
4700 unsigned int limit = 25, matches = 0;
4701
4702 if(argc > 1)
4703 {
4704 interval = ParseInterval(argv[1]);
4705 if(argc > 2)
4706 limit = atoi(argv[2]);
4707 }
4708
4709 intervalString(buffer, interval, user->handle_info);
4710 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4711
4712 for(cData = channelList; cData && matches < limit; cData = cData->next)
4713 {
4714 if((now - cData->visited) < interval)
4715 continue;
4716
4717 intervalString(buffer, now - cData->visited, user->handle_info);
4718 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4719 matches++;
4720 }
4721
4722 return 1;
4723}
4724
4725static MODCMD_FUNC(chan_opt_defaulttopic)
4726{
4727 if(argc > 1)
4728 {
4729 char *topic;
4730
4731 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4732 {
4733 reply("CSMSG_TOPIC_LOCKED", channel->name);
4734 return 0;
4735 }
4736
4737 topic = unsplit_string(argv+1, argc-1, NULL);
4738
4739 free(channel->channel_info->topic);
4740 if(topic[0] == '*' && topic[1] == 0)
4741 {
4742 topic = channel->channel_info->topic = NULL;
4743 }
4744 else
4745 {
4746 topic = channel->channel_info->topic = strdup(topic);
4747 if(channel->channel_info->topic_mask
4748 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4749 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4750 }
4751 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4752 }
4753
4754 if(channel->channel_info->topic)
4755 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4756 else
4757 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4758 return 1;
4759}
4760
4761static MODCMD_FUNC(chan_opt_topicmask)
4762{
4763 if(argc > 1)
4764 {
4765 struct chanData *cData = channel->channel_info;
4766 char *mask;
4767
4768 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4769 {
4770 reply("CSMSG_TOPIC_LOCKED", channel->name);
4771 return 0;
4772 }
4773
4774 mask = unsplit_string(argv+1, argc-1, NULL);
4775
4776 if(cData->topic_mask)
4777 free(cData->topic_mask);
4778 if(mask[0] == '*' && mask[1] == 0)
4779 {
4780 cData->topic_mask = 0;
4781 }
4782 else
4783 {
4784 cData->topic_mask = strdup(mask);
4785 if(!cData->topic)
4786 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4787 else if(!match_ircglob(cData->topic, cData->topic_mask))
4788 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4789 }
4790 }
4791
4792 if(channel->channel_info->topic_mask)
4793 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4794 else
4795 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4796 return 1;
4797}
4798
4799int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4800{
4801 if(argc > 1)
4802 {
4803 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4804 char *previous;
4805
4806 previous = *data;
4807 if(greeting[0] == '*' && greeting[1] == 0)
4808 *data = NULL;
4809 else
4810 {
4811 unsigned int length = strlen(greeting);
4812 if(length > chanserv_conf.greeting_length)
4813 {
4814 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4815 return 0;
4816 }
4817 *data = strdup(greeting);
4818 }
4819 if(previous)
4820 free(previous);
4821 }
4822
4823 if(*data)
4824 reply(name, *data);
4825 else
4826 reply(name, user_find_message(user, "MSG_NONE"));
4827 return 1;
4828}
4829
4830static MODCMD_FUNC(chan_opt_greeting)
4831{
4832 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4833}
4834
4835static MODCMD_FUNC(chan_opt_usergreeting)
4836{
4837 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4838}
4839
4840static MODCMD_FUNC(chan_opt_modes)
4841{
4842 struct mod_chanmode *new_modes;
4843 char modes[MODELEN];
4844
4845 if(argc > 1)
4846 {
4847 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4848 {
4849 reply("CSMSG_NO_ACCESS");
4850 return 0;
4851 }
4852 if(argv[1][0] == '*' && argv[1][1] == 0)
4853 {
4854 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4855 }
4856 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4857 {
4858 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4859 return 0;
4860 }
4861 else if(new_modes->argc > 1)
4862 {
4863 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4864 mod_chanmode_free(new_modes);
4865 return 0;
4866 }
4867 else
4868 {
4869 channel->channel_info->modes = *new_modes;
4870 modcmd_chanmode_announce(new_modes);
4871 mod_chanmode_free(new_modes);
4872 }
4873 }
4874
4875 mod_chanmode_format(&channel->channel_info->modes, modes);
4876 if(modes[0])
4877 reply("CSMSG_SET_MODES", modes);
4878 else
4879 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4880 return 1;
4881}
4882
4883#define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4884static int
4885channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4886{
4887 struct chanData *cData = channel->channel_info;
4888 int value;
4889
4890 if(argc > 1)
4891 {
4892 /* Set flag according to value. */
4893 if(enabled_string(argv[1]))
4894 {
4895 cData->flags |= mask;
4896 value = 1;
4897 }
4898 else if(disabled_string(argv[1]))
4899 {
4900 cData->flags &= ~mask;
4901 value = 0;
4902 }
4903 else
4904 {
4905 reply("MSG_INVALID_BINARY", argv[1]);
4906 return 0;
4907 }
4908 }
4909 else
4910 {
4911 /* Find current option value. */
4912 value = (cData->flags & mask) ? 1 : 0;
4913 }
4914
4915 if(value)
4916 reply(name, user_find_message(user, "MSG_ON"));
4917 else
4918 reply(name, user_find_message(user, "MSG_OFF"));
4919 return 1;
4920}
4921
4922static MODCMD_FUNC(chan_opt_nodelete)
4923{
4924 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4925 {
4926 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4927 return 0;
4928 }
4929
4930 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4931}
4932
4933static MODCMD_FUNC(chan_opt_dynlimit)
4934{
4935 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4936}
4937
4938static MODCMD_FUNC(chan_opt_offchannel)
4939{
4940 struct chanData *cData = channel->channel_info;
4941 int value;
4942
4943 if(argc > 1)
4944 {
4945 /* Set flag according to value. */
4946 if(enabled_string(argv[1]))
4947 {
4948 if(!IsOffChannel(cData))
4949 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4950 cData->flags |= CHANNEL_OFFCHANNEL;
4951 value = 1;
4952 }
4953 else if(disabled_string(argv[1]))
4954 {
4955 if(IsOffChannel(cData))
4956 {
4957 struct mod_chanmode change;
4958 mod_chanmode_init(&change);
4959 change.argc = 1;
4960 change.args[0].mode = MODE_CHANOP;
4961 change.args[0].member = AddChannelUser(chanserv, channel);
4962 mod_chanmode_announce(chanserv, channel, &change);
4963 }
4964 cData->flags &= ~CHANNEL_OFFCHANNEL;
4965 value = 0;
4966 }
4967 else
4968 {
4969 reply("MSG_INVALID_BINARY", argv[1]);
4970 return 0;
4971 }
4972 }
4973 else
4974 {
4975 /* Find current option value. */
4976 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4977 }
4978
4979 if(value)
4980 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4981 else
4982 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4983 return 1;
4984}
4985
4986static MODCMD_FUNC(chan_opt_defaults)
4987{
4988 struct userData *uData;
4989 struct chanData *cData;
4990 const char *confirm;
4991 enum levelOption lvlOpt;
4992 enum charOption chOpt;
4993
4994 cData = channel->channel_info;
4995 uData = GetChannelUser(cData, user->handle_info);
4996 if(!uData || (uData->access < UL_OWNER))
4997 {
4998 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4999 return 0;
5000 }
5001 confirm = make_confirmation_string(uData);
5002 if((argc < 2) || strcmp(argv[1], confirm))
5003 {
5004 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5005 return 0;
5006 }
5007 cData->flags = CHANNEL_DEFAULT_FLAGS;
5008 cData->modes = chanserv_conf.default_modes;
5009 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5010 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5011 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5012 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5013 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5014 return 1;
5015}
5016
5017static int
5018channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5019{
5020 struct chanData *cData = channel->channel_info;
5021 struct userData *uData;
5022 unsigned short value;
5023
5024 if(argc > 1)
5025 {
5026 if(!check_user_level(channel, user, option, 1, 1))
5027 {
5028 reply("CSMSG_CANNOT_SET");
5029 return 0;
5030 }
5031 value = user_level_from_name(argv[1], UL_OWNER+1);
5032 if(!value && strcmp(argv[1], "0"))
5033 {
5034 reply("CSMSG_INVALID_ACCESS", argv[1]);
5035 return 0;
5036 }
5037 uData = GetChannelUser(cData, user->handle_info);
5038 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5039 {
5040 reply("CSMSG_BAD_SETLEVEL");
5041 return 0;
5042 }
5043 switch(option)
5044 {
5045 case lvlGiveVoice:
5046 if(value > cData->lvlOpts[lvlGiveOps])
5047 {
5048 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5049 return 0;
5050 }
5051 break;
5052 case lvlGiveOps:
5053 if(value < cData->lvlOpts[lvlGiveVoice])
5054 {
5055 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5056 return 0;
5057 }
5058 break;
5059 case lvlSetters:
5060 /* This test only applies to owners, since non-owners
5061 * trying to set an option to above their level get caught
5062 * by the CSMSG_BAD_SETLEVEL test above.
5063 */
5064 if(value > uData->access)
5065 {
5066 reply("CSMSG_BAD_SETTERS");
5067 return 0;
5068 }
5069 break;
5070 default:
5071 break;
5072 }
5073 cData->lvlOpts[option] = value;
5074 }
5075 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5076 return argc > 1;
5077}
5078
5079static MODCMD_FUNC(chan_opt_enfops)
5080{
5081 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5082}
5083
5084static MODCMD_FUNC(chan_opt_giveops)
5085{
5086 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5087}
5088
5089static MODCMD_FUNC(chan_opt_enfmodes)
5090{
5091 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5092}
5093
5094static MODCMD_FUNC(chan_opt_enftopic)
5095{
5096 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5097}
5098
5099static MODCMD_FUNC(chan_opt_pubcmd)
5100{
5101 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5102}
5103
5104static MODCMD_FUNC(chan_opt_setters)
5105{
5106 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5107}
5108
5109static MODCMD_FUNC(chan_opt_ctcpusers)
5110{
5111 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5112}
5113
5114static MODCMD_FUNC(chan_opt_userinfo)
5115{
5116 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5117}
5118
5119static MODCMD_FUNC(chan_opt_givevoice)
5120{
5121 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5122}
5123
5124static MODCMD_FUNC(chan_opt_topicsnarf)
5125{
5126 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5127}
5128
5129static MODCMD_FUNC(chan_opt_inviteme)
5130{
5131 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5132}
5133
5134static int
5135channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5136{
5137 struct chanData *cData = channel->channel_info;
5138 int count = charOptions[option].count, index;
5139
5140 if(argc > 1)
5141 {
5142 index = atoi(argv[1]);
5143
5144 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5145 {
5146 reply("CSMSG_INVALID_NUMERIC", index);
5147 /* Show possible values. */
5148 for(index = 0; index < count; index++)
5149 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5150 return 0;
5151 }
5152
5153 cData->chOpts[option] = charOptions[option].values[index].value;
5154 }
5155 else
5156 {
5157 /* Find current option value. */
5158 find_value:
5159 for(index = 0;
5160 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5161 index++);
5162 if(index == count)
5163 {
5164 /* Somehow, the option value is corrupt; reset it to the default. */
5165 cData->chOpts[option] = charOptions[option].default_value;
5166 goto find_value;
5167 }
5168 }
5169
5170 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5171 return 1;
5172}
5173
5174static MODCMD_FUNC(chan_opt_protect)
5175{
5176 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5177}
5178
5179static MODCMD_FUNC(chan_opt_toys)
5180{
5181 return channel_multiple_option(chToys, CSFUNC_ARGS);
5182}
5183
5184static MODCMD_FUNC(chan_opt_ctcpreaction)
5185{
5186 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5187}
5188
5189static MODCMD_FUNC(chan_opt_topicrefresh)
5190{
5191 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5192}
5193
5194static struct svccmd_list set_shows_list;
5195
5196static void
5197handle_svccmd_unbind(struct svccmd *target) {
5198 unsigned int ii;
5199 for(ii=0; ii<set_shows_list.used; ++ii)
5200 if(target == set_shows_list.list[ii])
5201 set_shows_list.used = 0;
5202}
5203
5204static CHANSERV_FUNC(cmd_set)
5205{
5206 struct svccmd *subcmd;
5207 char buf[MAXLEN];
5208 unsigned int ii;
5209
5210 /* Check if we need to (re-)initialize set_shows_list. */
5211 if(!set_shows_list.used)
5212 {
5213 if(!set_shows_list.size)
5214 {
5215 set_shows_list.size = chanserv_conf.set_shows->used;
5216 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5217 }
5218 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5219 {
5220 const char *name = chanserv_conf.set_shows->list[ii];
5221 sprintf(buf, "%s %s", argv[0], name);
5222 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5223 if(!subcmd)
5224 {
5225 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5226 continue;
5227 }
5228 svccmd_list_append(&set_shows_list, subcmd);
5229 }
5230 }
5231
5232 if(argc < 2)
5233 {
5234 reply("CSMSG_CHANNEL_OPTIONS");
5235 for(ii = 0; ii < set_shows_list.used; ii++)
5236 {
5237 subcmd = set_shows_list.list[ii];
5238 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5239 }
5240 return 1;
5241 }
5242
5243 sprintf(buf, "%s %s", argv[0], argv[1]);
5244 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5245 if(!subcmd)
5246 {
5247 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5248 return 0;
5249 }
5250 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5251 {
5252 reply("CSMSG_NO_ACCESS");
5253 return 0;
5254 }
5255
5256 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5257}
5258
5259static int
5260user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5261{
5262 struct userData *uData;
5263
5264 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5265 if(!uData)
5266 {
5267 reply("CSMSG_NOT_USER", channel->name);
5268 return 0;
5269 }
5270
5271 if(argc < 2)
5272 {
5273 /* Just show current option value. */
5274 }
5275 else if(enabled_string(argv[1]))
5276 {
5277 uData->flags |= mask;
5278 }
5279 else if(disabled_string(argv[1]))
5280 {
5281 uData->flags &= ~mask;
5282 }
5283 else
5284 {
5285 reply("MSG_INVALID_BINARY", argv[1]);
5286 return 0;
5287 }
5288
5289 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5290 return 1;
5291}
5292
5293static MODCMD_FUNC(user_opt_noautoop)
5294{
5295 struct userData *uData;
5296
5297 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5298 if(!uData)
5299 {
5300 reply("CSMSG_NOT_USER", channel->name);
5301 return 0;
5302 }
5303 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5304 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5305 else
5306 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5307}
5308
5309static MODCMD_FUNC(user_opt_autoinvite)
5310{
5311 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5312}
5313
5314static MODCMD_FUNC(user_opt_info)
5315{
5316 struct userData *uData;
5317 char *infoline;
5318
5319 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5320
5321 if(!uData)
5322 {
5323 /* If they got past the command restrictions (which require access)
5324 * but fail this test, we have some fool with security override on.
5325 */
5326 reply("CSMSG_NOT_USER", channel->name);
5327 return 0;
5328 }
5329
5330 if(argc > 1)
5331 {
5332 size_t bp;
5333 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5334 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5335 {
5336 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5337 return 0;
5338 }
5339 bp = strcspn(infoline, "\001");
5340 if(infoline[bp])
5341 {
5342 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5343 return 0;
5344 }
5345 if(uData->info)
5346 free(uData->info);
5347 if(infoline[0] == '*' && infoline[1] == 0)
5348 uData->info = NULL;
5349 else
5350 uData->info = strdup(infoline);
5351 }
5352 if(uData->info)
5353 reply("CSMSG_USET_INFO", uData->info);
5354 else
5355 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5356 return 1;
5357}
5358
5359struct svccmd_list uset_shows_list;
5360
5361static CHANSERV_FUNC(cmd_uset)
5362{
5363 struct svccmd *subcmd;
5364 char buf[MAXLEN];
5365 unsigned int ii;
5366
5367 /* Check if we need to (re-)initialize uset_shows_list. */
5368 if(!uset_shows_list.used)
5369 {
5370 char *options[] =
5371 {
5372 "NoAutoOp", "AutoInvite", "Info"
5373 };
5374
5375 if(!uset_shows_list.size)
5376 {
5377 uset_shows_list.size = ArrayLength(options);
5378 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5379 }
5380 for(ii = 0; ii < ArrayLength(options); ii++)
5381 {
5382 const char *name = options[ii];
5383 sprintf(buf, "%s %s", argv[0], name);
5384 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5385 if(!subcmd)
5386 {
5387 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5388 continue;
5389 }
5390 svccmd_list_append(&uset_shows_list, subcmd);
5391 }
5392 }
5393
5394 if(argc < 2)
5395 {
5396 /* Do this so options are presented in a consistent order. */
5397 reply("CSMSG_USER_OPTIONS");
5398 for(ii = 0; ii < uset_shows_list.used; ii++)
5399 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5400 return 1;
5401 }
5402
5403 sprintf(buf, "%s %s", argv[0], argv[1]);
5404 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5405 if(!subcmd)
5406 {
5407 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5408 return 0;
5409 }
5410
5411 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5412}
5413
5414static CHANSERV_FUNC(cmd_giveownership)
5415{
5416 struct handle_info *new_owner_hi;
5417 struct userData *new_owner, *curr_user;
5418 struct chanData *cData = channel->channel_info;
5419 struct do_not_register *dnr;
5420 unsigned int force;
5421 unsigned short co_access;
5422 char reason[MAXLEN];
5423
5424 REQUIRE_PARAMS(2);
5425 curr_user = GetChannelAccess(cData, user->handle_info);
5426 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5427 if(!curr_user || (curr_user->access != UL_OWNER))
5428 {
5429 struct userData *owner = NULL;
5430 for(curr_user = channel->channel_info->users;
5431 curr_user;
5432 curr_user = curr_user->next)
5433 {
5434 if(curr_user->access != UL_OWNER)
5435 continue;
5436 if(owner)
5437 {
5438 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5439 return 0;
5440 }
5441 owner = curr_user;
5442 }
5443 curr_user = owner;
5444 }
5445 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5446 return 0;
5447 if(new_owner_hi == user->handle_info)
5448 {
5449 reply("CSMSG_NO_TRANSFER_SELF");
5450 return 0;
5451 }
5452 new_owner = GetChannelAccess(cData, new_owner_hi);
5453 if(!new_owner)
5454 {
5455 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5456 return 0;
5457 }
5458 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5459 {
5460 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5461 return 0;
5462 }
5463 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5464 if(!IsHelping(user))
5465 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5466 else
5467 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5468 return 0;
5469 }
5470 if(new_owner->access >= UL_COOWNER)
5471 co_access = new_owner->access;
5472 else
5473 co_access = UL_COOWNER;
5474 new_owner->access = UL_OWNER;
5475 if(curr_user)
5476 curr_user->access = co_access;
5477 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5478 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5479 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5480 return 1;
5481}
5482
5483static CHANSERV_FUNC(cmd_suspend)
5484{
5485 struct handle_info *hi;
5486 struct userData *self, *target;
5487
5488 REQUIRE_PARAMS(2);
5489 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5490 self = GetChannelUser(channel->channel_info, user->handle_info);
5491 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5492 {
5493 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5494 return 0;
5495 }
5496 if(target->access >= self->access)
5497 {
5498 reply("MSG_USER_OUTRANKED", hi->handle);
5499 return 0;
5500 }
5501 if(target->flags & USER_SUSPENDED)
5502 {
5503 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5504 return 0;
5505 }
5506 if(target->present)
5507 {
5508 target->present = 0;
5509 target->seen = now;
5510 }
5511 target->flags |= USER_SUSPENDED;
5512 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5513 return 1;
5514}
5515
5516static CHANSERV_FUNC(cmd_unsuspend)
5517{
5518 struct handle_info *hi;
5519 struct userData *self, *target;
5520
5521 REQUIRE_PARAMS(2);
5522 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5523 self = GetChannelUser(channel->channel_info, user->handle_info);
5524 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5525 {
5526 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5527 return 0;
5528 }
5529 if(target->access >= self->access)
5530 {
5531 reply("MSG_USER_OUTRANKED", hi->handle);
5532 return 0;
5533 }
5534 if(!(target->flags & USER_SUSPENDED))
5535 {
5536 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5537 return 0;
5538 }
5539 target->flags &= ~USER_SUSPENDED;
5540 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5541 return 1;
5542}
5543
5544static MODCMD_FUNC(cmd_deleteme)
5545{
5546 struct handle_info *hi;
5547 struct userData *target;
5548 const char *confirm_string;
5549 unsigned short access;
5550 char *channel_name;
5551
5552 hi = user->handle_info;
5553 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5554 {
5555 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5556 return 0;
5557 }
5558 if(target->access == UL_OWNER)
5559 {
5560 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5561 return 0;
5562 }
5563 confirm_string = make_confirmation_string(target);
5564 if((argc < 2) || strcmp(argv[1], confirm_string))
5565 {
5566 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5567 return 0;
5568 }
5569 access = target->access;
5570 channel_name = strdup(channel->name);
5571 del_channel_user(target, 1);
5572 reply("CSMSG_DELETED_YOU", access, channel_name);
5573 free(channel_name);
5574 return 1;
5575}
5576
5577static void
5578chanserv_refresh_topics(UNUSED_ARG(void *data))
5579{
5580 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5581 struct chanData *cData;
5582 char opt;
5583
5584 for(cData = channelList; cData; cData = cData->next)
5585 {
5586 if(IsSuspended(cData))
5587 continue;
5588 opt = cData->chOpts[chTopicRefresh];
5589 if(opt == 'n')
5590 continue;
5591 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5592 continue;
5593 if(cData->topic)
5594 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5595 cData->last_refresh = refresh_num;
5596 }
5597 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5598}
5599
5600static CHANSERV_FUNC(cmd_unf)
5601{
5602 if(channel)
5603 {
5604 char response[MAXLEN];
5605 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5606 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5607 irc_privmsg(cmd->parent->bot, channel->name, response);
5608 }
5609 else
5610 reply("CSMSG_UNF_RESPONSE");
5611 return 1;
5612}
5613
5614static CHANSERV_FUNC(cmd_ping)
5615{
5616 if(channel)
5617 {
5618 char response[MAXLEN];
5619 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5620 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5621 irc_privmsg(cmd->parent->bot, channel->name, response);
5622 }
5623 else
5624 reply("CSMSG_PING_RESPONSE");
5625 return 1;
5626}
5627
5628static CHANSERV_FUNC(cmd_wut)
5629{
5630 if(channel)
5631 {
5632 char response[MAXLEN];
5633 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5634 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5635 irc_privmsg(cmd->parent->bot, channel->name, response);
5636 }
5637 else
5638 reply("CSMSG_WUT_RESPONSE");
5639 return 1;
5640}
5641
5642static CHANSERV_FUNC(cmd_8ball)
5643{
5644 unsigned int i, j, accum;
5645 const char *resp;
5646
5647 REQUIRE_PARAMS(2);
5648 accum = 0;
5649 for(i=1; i<argc; i++)
5650 for(j=0; argv[i][j]; j++)
5651 accum = (accum << 5) - accum + toupper(argv[i][j]);
5652 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5653 if(channel)
5654 {
5655 char response[MAXLEN];
5656 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
5657 irc_privmsg(cmd->parent->bot, channel->name, response);
5658 }
5659 else
5660 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5661 return 1;
5662}
5663
5664static CHANSERV_FUNC(cmd_d)
5665{
5666 unsigned long sides, count, modifier, ii, total;
5667 char response[MAXLEN], *sep;
5668 const char *fmt;
5669
5670 REQUIRE_PARAMS(2);
5671 if((count = strtoul(argv[1], &sep, 10)) < 1)
5672 goto no_dice;
5673 if(sep[0] == 0)
5674 {
5675 if(count == 1)
5676 goto no_dice;
5677 sides = count;
5678 count = 1;
5679 modifier = 0;
5680 }
5681 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5682 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5683 {
5684 if(sep[0] == 0)
5685 modifier = 0;
5686 else if((sep[0] == '-') && isdigit(sep[1]))
5687 modifier = strtoul(sep, NULL, 10);
5688 else if((sep[0] == '+') && isdigit(sep[1]))
5689 modifier = strtoul(sep+1, NULL, 10);
5690 else
5691 goto no_dice;
5692 }
5693 else
5694 {
5695 no_dice:
5696 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5697 return 0;
5698 }
5699 if(count > 10)
5700 {
5701 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5702 return 0;
5703 }
5704 for(total = ii = 0; ii < count; ++ii)
5705 total += (rand() % sides) + 1;
5706 total += modifier;
5707
5708 if((count > 1) || modifier)
5709 {
5710 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5711 sprintf(response, fmt, total, count, sides, modifier);
5712 }
5713 else
5714 {
5715 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5716 sprintf(response, fmt, total, sides);
5717 }
5718 if(channel)
5719 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5720 else
5721 send_message_type(4, user, cmd->parent->bot, "%s", response);
5722 return 1;
5723}
5724
5725static CHANSERV_FUNC(cmd_huggle)
5726{
5727 /* CTCP must be via PRIVMSG, never notice */
5728 if(channel)
5729 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5730 else
5731 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5732 return 1;
5733}
5734
5735static void
5736chanserv_adjust_limit(void *data)
5737{
5738 struct mod_chanmode change;
5739 struct chanData *cData = data;
5740 struct chanNode *channel = cData->channel;
5741 unsigned int limit;
5742
5743 if(IsSuspended(cData))
5744 return;
5745
5746 cData->limitAdjusted = now;
5747 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5748 if(cData->modes.modes_set & MODE_LIMIT)
5749 {
5750 if(limit > cData->modes.new_limit)
5751 limit = cData->modes.new_limit;
5752 else if(limit == cData->modes.new_limit)
5753 return;
5754 }
5755
5756 mod_chanmode_init(&change);
5757 change.modes_set = MODE_LIMIT;
5758 change.new_limit = limit;
5759 mod_chanmode_announce(chanserv, channel, &change);
5760}
5761
5762static void
5763handle_new_channel(struct chanNode *channel)
5764{
5765 struct chanData *cData;
5766
5767 if(!(cData = channel->channel_info))
5768 return;
5769
5770 if(cData->modes.modes_set || cData->modes.modes_clear)
5771 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5772
5773 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5774 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5775}
5776
5777/* Welcome to my worst nightmare. Warning: Read (or modify)
5778 the code below at your own risk. */
5779static int
5780handle_join(struct modeNode *mNode)
5781{
5782 struct mod_chanmode change;
5783 struct userNode *user = mNode->user;
5784 struct chanNode *channel = mNode->channel;
5785 struct chanData *cData;
5786 struct userData *uData = NULL;
5787 struct banData *bData;
5788 struct handle_info *handle;
5789 unsigned int modes = 0, info = 0;
5790 char *greeting;
5791
5792 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5793 return 0;
5794
5795 cData = channel->channel_info;
5796 if(channel->members.used > cData->max)
5797 cData->max = channel->members.used;
5798
5799 /* Check for bans. If they're joining through a ban, one of two
5800 * cases applies:
5801 * 1: Join during a netburst, by riding the break. Kick them
5802 * unless they have ops or voice in the channel.
5803 * 2: They're allowed to join through the ban (an invite in
5804 * ircu2.10, or a +e on Hybrid, or something).
5805 * If they're not joining through a ban, and the banlist is not
5806 * full, see if they're on the banlist for the channel. If so,
5807 * kickban them.
5808 */
5809 if(user->uplink->burst && !mNode->modes)
5810 {
5811 unsigned int ii;
5812 for(ii = 0; ii < channel->banlist.used; ii++)
5813 {
5814 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5815 {
5816 /* Riding a netburst. Naughty. */
5817 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5818 return 1;
5819 }
5820 }
5821 }
5822
5823 mod_chanmode_init(&change);
5824 change.argc = 1;
5825 if(channel->banlist.used < MAXBANS)
5826 {
5827 /* Not joining through a ban. */
5828 for(bData = cData->bans;
5829 bData && !user_matches_glob(user, bData->mask, 1);
5830 bData = bData->next);
5831
5832 if(bData)
5833 {
5834 char kick_reason[MAXLEN];
5835 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5836
5837 bData->triggered = now;
5838 if(bData != cData->bans)
5839 {
5840 /* Shuffle the ban to the head of the list. */
5841 if(bData->next)
5842 bData->next->prev = bData->prev;
5843 if(bData->prev)
5844 bData->prev->next = bData->next;
5845
5846 bData->prev = NULL;
5847 bData->next = cData->bans;
5848
5849 if(cData->bans)
5850 cData->bans->prev = bData;
5851 cData->bans = bData;
5852 }
5853
5854 change.args[0].mode = MODE_BAN;
5855 change.args[0].hostmask = bData->mask;
5856 mod_chanmode_announce(chanserv, channel, &change);
5857 KickChannelUser(user, channel, chanserv, kick_reason);
5858 return 1;
5859 }
5860 }
5861
5862 /* ChanServ will not modify the limits in join-flooded channels.
5863 It will also skip DynLimit processing when the user (or srvx)
5864 is bursting in, because there are likely more incoming. */
5865 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5866 && !user->uplink->burst
5867 && !channel->join_flooded
5868 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5869 {
5870 /* The user count has begun "bumping" into the channel limit,
5871 so set a timer to raise the limit a bit. Any previous
5872 timers are removed so three incoming users within the delay
5873 results in one limit change, not three. */
5874
5875 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5876 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5877 }
5878
5879 if(channel->join_flooded)
5880 {
5881 /* don't automatically give ops or voice during a join flood */
5882 }
5883 else if(cData->lvlOpts[lvlGiveOps] == 0)
5884 modes |= MODE_CHANOP;
5885 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5886 modes |= MODE_VOICE;
5887
5888 greeting = cData->greeting;
5889 if(user->handle_info)
5890 {
5891 handle = user->handle_info;
5892
5893 if(IsHelper(user) && !IsHelping(user))
5894 {
5895 unsigned int ii;
5896 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5897 {
5898 if(channel == chanserv_conf.support_channels.list[ii])
5899 {
5900 HANDLE_SET_FLAG(user->handle_info, HELPING);
5901 break;
5902 }
5903 }
5904 }
5905
5906 uData = GetTrueChannelAccess(cData, handle);
5907 if(uData && !IsUserSuspended(uData))
5908 {
5909 /* Ops and above were handled by the above case. */
5910 if(IsUserAutoOp(uData))
5911 {
5912 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5913 modes |= MODE_CHANOP;
5914 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5915 modes |= MODE_VOICE;
5916 }
5917 if(uData->access >= UL_PRESENT)
5918 cData->visited = now;
5919 if(cData->user_greeting)
5920 greeting = cData->user_greeting;
5921 if(uData->info
5922 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5923 && ((now - uData->seen) >= chanserv_conf.info_delay)
5924 && !uData->present)
5925 info = 1;
5926 uData->seen = now;
5927 uData->present = 1;
5928 }
5929 }
5930 if(!user->uplink->burst)
5931 {
5932 if(modes)
5933 {
5934 if(modes & MODE_CHANOP)
5935 modes &= ~MODE_VOICE;
5936 change.args[0].mode = modes;
5937 change.args[0].member = mNode;
5938 mod_chanmode_announce(chanserv, channel, &change);
5939 }
5940 if(greeting && !user->uplink->burst)
5941 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5942 if(uData && info)
5943 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5944 }
5945 return 0;
5946}
5947
5948static void
5949handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5950{
5951 struct mod_chanmode change;
5952 struct userData *channel;
5953 unsigned int ii, jj;
5954
5955 if(!user->handle_info)
5956 return;
5957
5958 mod_chanmode_init(&change);
5959 change.argc = 1;
5960 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5961 {
5962 struct chanNode *cn;
5963 struct modeNode *mn;
5964 if(IsUserSuspended(channel)
5965 || IsSuspended(channel->channel)
5966 || !(cn = channel->channel->channel))
5967 continue;
5968
5969 mn = GetUserMode(cn, user);
5970 if(!mn)
5971 {
5972 if(!IsUserSuspended(channel)
5973 && IsUserAutoInvite(channel)
5974 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5975 && !self->burst
5976 && !user->uplink->burst)
5977 irc_invite(chanserv, user, cn);
5978 continue;
5979 }
5980
5981 if(channel->access >= UL_PRESENT)
5982 channel->channel->visited = now;
5983
5984 if(IsUserAutoOp(channel))
5985 {
5986 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5987 change.args[0].mode = MODE_CHANOP;
5988 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5989 change.args[0].mode = MODE_VOICE;
5990 else
5991 change.args[0].mode = 0;
5992 change.args[0].member = mn;
5993 if(change.args[0].mode)
5994 mod_chanmode_announce(chanserv, cn, &change);
5995 }
5996
5997 channel->seen = now;
5998 channel->present = 1;
5999 }
6000
6001 for(ii = 0; ii < user->channels.used; ++ii)
6002 {
6003 struct chanNode *channel = user->channels.list[ii]->channel;
6004 struct banData *ban;
6005
6006 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6007 || !channel->channel_info)
6008 continue;
6009 for(jj = 0; jj < channel->banlist.used; ++jj)
6010 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6011 break;
6012 if(jj < channel->banlist.used)
6013 continue;
6014 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6015 {
6016 char kick_reason[MAXLEN];
6017 if(!user_matches_glob(user, ban->mask, 1))
6018 continue;
6019 change.args[0].mode = MODE_BAN;
6020 change.args[0].hostmask = ban->mask;
6021 mod_chanmode_announce(chanserv, channel, &change);
6022 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6023 KickChannelUser(user, channel, chanserv, kick_reason);
6024 ban->triggered = now;
6025 break;
6026 }
6027 }
6028
6029 if(IsSupportHelper(user))
6030 {
6031 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6032 {
6033 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6034 {
6035 HANDLE_SET_FLAG(user->handle_info, HELPING);
6036 break;
6037 }
6038 }
6039 }
6040}
6041
6042static void
6043handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6044{
6045 struct chanData *cData;
6046 struct userData *uData;
6047
6048 cData = mn->channel->channel_info;
6049 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6050 return;
6051
6052 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6053 {
6054 /* Allow for a bit of padding so that the limit doesn't
6055 track the user count exactly, which could get annoying. */
6056 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6057 {
6058 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6059 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6060 }
6061 }
6062
6063 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6064 {
6065 scan_user_presence(uData, mn->user);
6066 uData->seen = now;
6067 }
6068
6069 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6070 {
6071 unsigned int ii, jj;
6072 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6073 {
6074 for(jj = 0; jj < mn->user->channels.used; ++jj)
6075 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6076 break;
6077 if(jj < mn->user->channels.used)
6078 break;
6079 }
6080 if(ii == chanserv_conf.support_channels.used)
6081 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6082 }
6083}
6084
6085static void
6086handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6087{
6088 struct userData *uData;
6089
6090 if(!channel->channel_info || !kicker || IsService(kicker)
6091 || (kicker == victim) || IsSuspended(channel->channel_info)
6092 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6093 return;
6094
6095 if(protect_user(victim, kicker, channel->channel_info))
6096 {
6097 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6098 KickChannelUser(kicker, channel, chanserv, reason);
6099 }
6100
6101 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6102 uData->seen = now;
6103}
6104
6105static int
6106handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6107{
6108 struct chanData *cData;
6109
6110 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6111 return 0;
6112
6113 cData = channel->channel_info;
6114 if(bad_topic(channel, user, channel->topic))
6115 {
6116 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6117 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6118 SetChannelTopic(channel, chanserv, old_topic, 1);
6119 else if(cData->topic)
6120 SetChannelTopic(channel, chanserv, cData->topic, 1);
6121 return 1;
6122 }
6123 /* With topicsnarf, grab the topic and save it as the default topic. */
6124 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6125 {
6126 free(cData->topic);
6127 cData->topic = strdup(channel->topic);
6128 }
6129 return 0;
6130}
6131
6132static void
6133handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6134{
6135 struct mod_chanmode *bounce = NULL;
6136 unsigned int bnc, ii;
6137 char deopped = 0;
6138
6139 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6140 return;
6141
6142 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6143 && mode_lock_violated(&channel->channel_info->modes, change))
6144 {
6145 char correct[MAXLEN];
6146 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6147 mod_chanmode_format(&channel->channel_info->modes, correct);
6148 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6149 }
6150 for(ii = bnc = 0; ii < change->argc; ++ii)
6151 {
6152 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6153 {
6154 const struct userNode *victim = change->args[ii].member->user;
6155 if(!protect_user(victim, user, channel->channel_info))
6156 continue;
6157 if(!bounce)
6158 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6159 if(!deopped)
6160 {
6161 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6162 bounce->args[bnc].member = GetUserMode(channel, user);
6163 if(bounce->args[bnc].member)
6164 bnc++;
6165 deopped = 1;
6166 }
6167 bounce->args[bnc].mode = MODE_CHANOP;
6168 bounce->args[bnc].member = change->args[ii].member;
6169 bnc++;
6170 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6171 }
6172 else if(change->args[ii].mode & MODE_CHANOP)
6173 {
6174 const struct userNode *victim = change->args[ii].member->user;
6175 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6176 continue;
6177 if(!bounce)
6178 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6179 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6180 bounce->args[bnc].member = change->args[ii].member;
6181 bnc++;
6182 }
6183 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6184 {
6185 const char *ban = change->args[ii].hostmask;
6186 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6187 continue;
6188 if(!bounce)
6189 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6190 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6191 bounce->args[bnc].hostmask = ban;
6192 bnc++;
6193 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6194 }
6195 }
6196 if(bounce)
6197 {
6198 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6199 mod_chanmode_announce(chanserv, channel, bounce);
6200 mod_chanmode_free(bounce);
6201 }
6202}
6203
6204static void
6205handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6206{
6207 struct chanNode *channel;
6208 struct banData *bData;
6209 struct mod_chanmode change;
6210 unsigned int ii, jj;
6211 char kick_reason[MAXLEN];
6212
6213 mod_chanmode_init(&change);
6214 change.argc = 1;
6215 change.args[0].mode = MODE_BAN;
6216 for(ii = 0; ii < user->channels.used; ++ii)
6217 {
6218 channel = user->channels.list[ii]->channel;
6219 /* Need not check for bans if they're opped or voiced. */
6220 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6221 continue;
6222 /* Need not check for bans unless channel registration is active. */
6223 if(!channel->channel_info || IsSuspended(channel->channel_info))
6224 continue;
6225 /* Look for a matching ban already on the channel. */
6226 for(jj = 0; jj < channel->banlist.used; ++jj)
6227 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6228 break;
6229 /* Need not act if we found one. */
6230 if(jj < channel->banlist.used)
6231 continue;
6232 /* Look for a matching ban in this channel. */
6233 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6234 {
6235 if(!user_matches_glob(user, bData->mask, 1))
6236 continue;
6237 change.args[0].hostmask = bData->mask;
6238 mod_chanmode_announce(chanserv, channel, &change);
6239 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6240 KickChannelUser(user, channel, chanserv, kick_reason);
6241 bData->triggered = now;
6242 break; /* we don't need to check any more bans in the channel */
6243 }
6244 }
6245}
6246
6247static void handle_rename(struct handle_info *handle, const char *old_handle)
6248{
6249 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6250
6251 if(dnr)
6252 {
6253 dict_remove2(handle_dnrs, old_handle, 1);
6254 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6255 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6256 }
6257}
6258
6259static void
6260handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6261{
6262 struct userNode *h_user;
6263
6264 if(handle->channels)
6265 {
6266 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6267 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6268
6269 while(handle->channels)
6270 del_channel_user(handle->channels, 1);
6271 }
6272}
6273
6274static void
6275handle_server_link(UNUSED_ARG(struct server *server))
6276{
6277 struct chanData *cData;
6278
6279 for(cData = channelList; cData; cData = cData->next)
6280 {
6281 if(!IsSuspended(cData))
6282 cData->may_opchan = 1;
6283 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6284 && !cData->channel->join_flooded
6285 && ((cData->channel->limit - cData->channel->members.used)
6286 < chanserv_conf.adjust_threshold))
6287 {
6288 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6289 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6290 }
6291 }
6292}
6293
6294static void
6295chanserv_conf_read(void)
6296{
6297 dict_t conf_node;
6298 const char *str;
6299 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6300 struct mod_chanmode *change;
6301 struct string_list *strlist;
6302 struct chanNode *chan;
6303 unsigned int ii;
6304
6305 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6306 {
6307 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6308 return;
6309 }
6310 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6311 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6312 chanserv_conf.support_channels.used = 0;
6313 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6314 {
6315 for(ii = 0; ii < strlist->used; ++ii)
6316 {
6317 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6318 if(!str2)
6319 str2 = "+nt";
6320 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6321 LockChannel(chan);
6322 channelList_append(&chanserv_conf.support_channels, chan);
6323 }
6324 }
6325 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6326 {
6327 const char *str2;
6328 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6329 if(!str2)
6330 str2 = "+nt";
6331 chan = AddChannel(str, now, str2, NULL);
6332 LockChannel(chan);
6333 channelList_append(&chanserv_conf.support_channels, chan);
6334 }
6335 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6336 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6337 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6338 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6339 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6340 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6341 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6342 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6343 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6344 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6345 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6346 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6347 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6348 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6349 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6350 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6351 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6352 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6353 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6354 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6355 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6356 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6357 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6358 if(chanserv && str)
6359 NickChange(chanserv, str, 0);
6360 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6361 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6362 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6363 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6364 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6365 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6366 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6367 chanserv_conf.max_owned = str ? atoi(str) : 5;
6368 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6369 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6370 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6371 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6372 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6373 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6374 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6375 if(!str)
6376 str = "+nt";
6377 safestrncpy(mode_line, str, sizeof(mode_line));
6378 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6379 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6380 {
6381 chanserv_conf.default_modes = *change;
6382 mod_chanmode_free(change);
6383 }
6384 free_string_list(chanserv_conf.set_shows);
6385 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6386 if(strlist)
6387 strlist = string_list_copy(strlist);
6388 else
6389 {
6390 static const char *list[] = {
6391 /* free form text */
6392 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6393 /* options based on user level */
6394 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6395 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6396 /* multiple choice options */
6397 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6398 /* binary options */
6399 "DynLimit", "NoDelete",
6400 /* delimiter */
6401 NULL
6402 };
6403 unsigned int ii;
6404 strlist = alloc_string_list(ArrayLength(list)-1);
6405 for(ii=0; list[ii]; ii++)
6406 string_list_append(strlist, strdup(list[ii]));
6407 }
6408 chanserv_conf.set_shows = strlist;
6409 /* We don't look things up now, in case the list refers to options
6410 * defined by modules initialized after this point. Just mark the
6411 * function list as invalid, so it will be initialized.
6412 */
6413 set_shows_list.used = 0;
6414 free_string_list(chanserv_conf.eightball);
6415 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6416 if(strlist)
6417 {
6418 strlist = string_list_copy(strlist);
6419 }
6420 else
6421 {
6422 strlist = alloc_string_list(4);
6423 string_list_append(strlist, strdup("Yes."));
6424 string_list_append(strlist, strdup("No."));
6425 string_list_append(strlist, strdup("Maybe so."));
6426 }
6427 chanserv_conf.eightball = strlist;
6428 free_string_list(chanserv_conf.old_ban_names);
6429 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6430 if(strlist)
6431 strlist = string_list_copy(strlist);
6432 else
6433 strlist = alloc_string_list(2);
6434 chanserv_conf.old_ban_names = strlist;
6435 /* the variable itself is actually declared in proto-common.c; this is equally
6436 * parse issue. */
6437 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6438 off_channel = (str && enabled_string(str)) ? 1 : 0;
6439}
6440
6441static void
6442chanserv_note_type_read(const char *key, struct record_data *rd)
6443{
6444 dict_t obj;
6445 struct note_type *ntype;
6446 const char *str;
6447
6448 if(!(obj = GET_RECORD_OBJECT(rd)))
6449 {
6450 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6451 return;
6452 }
6453 if(!(ntype = chanserv_create_note_type(key)))
6454 {
6455 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6456 return;
6457 }
6458
6459 /* Figure out set access */
6460 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6461 {
6462 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6463 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6464 }
6465 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6466 {
6467 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6468 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6469 }
6470 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6471 {
6472 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6473 }
6474 else
6475 {
6476 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6477 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6478 ntype->set_access.min_opserv = 0;
6479 }
6480
6481 /* Figure out visibility */
6482 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6483 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6484 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6485 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6486 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6487 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6488 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6489 ntype->visible_type = NOTE_VIS_ALL;
6490 else
6491 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6492
6493 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6494 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6495}
6496
6497static void
6498user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6499{
6500 struct handle_info *handle;
6501 struct userData *uData;
6502 char *seen, *inf, *flags;
6503 time_t last_seen;
6504 unsigned short access;
6505
6506 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6507 {
6508 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6509 return;
6510 }
6511
6512 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6513 if(access > UL_OWNER)
6514 {
6515 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6516 return;
6517 }
6518
6519 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6520 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6521 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6522 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6523 handle = get_handle_info(key);
6524 if(!handle)
6525 {
6526 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6527 return;
6528 }
6529
6530 uData = add_channel_user(chan, handle, access, last_seen, inf);
6531 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6532}
6533
6534static void
6535ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6536{
6537 struct banData *bData;
6538 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6539 time_t set_time, triggered_time, expires_time;
6540
6541 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6542 {
6543 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6544 return;
6545 }
6546
6547 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6548 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6549 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6550 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6551 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6552 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6553
6554 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6555 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6556 if(s_expires)
6557 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6558 else if(s_duration)
6559 expires_time = set_time + atoi(s_duration);
6560 else
6561 expires_time = 0;
6562
6563 if(expires_time && (expires_time < now))
6564 return;
6565
6566 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6567}
6568
6569static struct suspended *
6570chanserv_read_suspended(dict_t obj)
6571{
6572 struct suspended *suspended = calloc(1, sizeof(*suspended));
6573 char *str;
6574 dict_t previous;
6575
6576 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6577 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6578 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6579 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6580 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6581 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6582 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6583 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6584 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6585 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6586 return suspended;
6587}
6588
6589static int
6590chanserv_channel_read(const char *key, struct record_data *hir)
6591{
6592 struct suspended *suspended;
6593 struct mod_chanmode *modes;
6594 struct chanNode *cNode;
6595 struct chanData *cData;
6596 struct dict *channel, *obj;
6597 char *str, *argv[10];
6598 dict_iterator_t it;
6599 unsigned int argc;
6600
6601 channel = hir->d.object;
6602
6603 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6604 if(!str)
6605 str = "<unknown>";
6606 cNode = AddChannel(key, now, NULL, NULL);
6607 if(!cNode)
6608 {
6609 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6610 return 0;
6611 }
6612 cData = register_channel(cNode, str);
6613 if(!cData)
6614 {
6615 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6616 return 0;
6617 }
6618
6619 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6620 {
6621 enum levelOption lvlOpt;
6622 enum charOption chOpt;
6623
6624 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6625 cData->flags = atoi(str);
6626
6627 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6628 {
6629 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6630 if(str)
6631 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6632 else if(levelOptions[lvlOpt].old_flag)
6633 {
6634 if(cData->flags & levelOptions[lvlOpt].old_flag)
6635 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6636 else
6637 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6638 }
6639 }
6640
6641 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6642 {
6643 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6644 continue;
6645 cData->chOpts[chOpt] = str[0];
6646 }
6647 }
6648 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6649 {
6650 enum levelOption lvlOpt;
6651 enum charOption chOpt;
6652 unsigned int count;
6653
6654 cData->flags = base64toint(str, 5);
6655 count = strlen(str += 5);
6656 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6657 {
6658 unsigned short lvl;
6659 if(levelOptions[lvlOpt].old_flag)
6660 {
6661 if(cData->flags & levelOptions[lvlOpt].old_flag)
6662 lvl = levelOptions[lvlOpt].flag_value;
6663 else
6664 lvl = levelOptions[lvlOpt].default_value;
6665 }
6666 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6667 {
6668 case 'c': lvl = UL_COOWNER; break;
4048352e 6669 case 'm': lvl = UL_MANAGER; break;
d76ed9a9 6670 case 'n': lvl = UL_OWNER+1; break;
6671 case 'o': lvl = UL_OP; break;
6672 case 'p': lvl = UL_PEON; break;
6673 case 'w': lvl = UL_OWNER; break;
6674 default: lvl = 0; break;
6675 }
6676 cData->lvlOpts[lvlOpt] = lvl;
6677 }
6678 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6679 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6680 }
6681
6682 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6683 {
6684 suspended = chanserv_read_suspended(obj);
6685 cData->suspended = suspended;
6686 suspended->cData = cData;
6687 /* We could use suspended->expires and suspended->revoked to
6688 * set the CHANNEL_SUSPENDED flag, but we don't. */
6689 }
6690 else if(IsSuspended(cData))
6691 {
6692 suspended = calloc(1, sizeof(*suspended));
6693 suspended->issued = 0;
6694 suspended->revoked = 0;
6695 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6696 suspended->expires = str ? atoi(str) : 0;
6697 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6698 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6699 suspended->reason = strdup(str ? str : "No reason");
6700 suspended->previous = NULL;
6701 cData->suspended = suspended;
6702 suspended->cData = cData;
6703 }
6704 else
6705 suspended = NULL; /* to squelch a warning */
6706
6707 if(IsSuspended(cData)) {
6708 if(suspended->expires > now)
6709 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6710 else if(suspended->expires)
6711 cData->flags &= ~CHANNEL_SUSPENDED;
6712 }
6713
6714 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6715 struct mod_chanmode change;
6716 mod_chanmode_init(&change);
6717 change.argc = 1;
6718 change.args[0].mode = MODE_CHANOP;
6719 change.args[0].member = AddChannelUser(chanserv, cNode);
6720 mod_chanmode_announce(chanserv, cNode, &change);
6721 }
6722
6723 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6724 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6725 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6726 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6727 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6728 cData->max = str ? atoi(str) : 0;
6729 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6730 cData->greeting = str ? strdup(str) : NULL;
6731 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6732 cData->user_greeting = str ? strdup(str) : NULL;
6733 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6734 cData->topic_mask = str ? strdup(str) : NULL;
6735 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6736 cData->topic = str ? strdup(str) : NULL;
6737
6738 if(!IsSuspended(cData)
6739 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6740 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6741 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6742 cData->modes = *modes;
6743 cData->modes.modes_set |= MODE_REGISTERED;
6744 if(cData->modes.argc > 1)
6745 cData->modes.argc = 1;
6746 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6747 mod_chanmode_free(modes);
6748 }
6749
6750 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6751 for(it = dict_first(obj); it; it = iter_next(it))
6752 user_read_helper(iter_key(it), iter_data(it), cData);
6753
6754 if(!cData->users && !IsProtected(cData))
6755 {
6756 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6757 unregister_channel(cData, "has empty user list.");
6758 return 0;
6759 }
6760
6761 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6762 for(it = dict_first(obj); it; it = iter_next(it))
6763 ban_read_helper(iter_key(it), iter_data(it), cData);
6764
6765 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6766 for(it = dict_first(obj); it; it = iter_next(it))
6767 {
6768 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6769 struct record_data *rd = iter_data(it);
6770 const char *note, *setter;
6771
6772 if(rd->type != RECDB_OBJECT)
6773 {
6774 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6775 }
6776 else if(!ntype)
6777 {
6778 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6779 }
6780 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6781 {
6782 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6783 }
6784 else
6785 {
6786 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6787 if(!setter) setter = "<unknown>";
6788 chanserv_add_channel_note(cData, ntype, setter, note);
6789 }
6790 }
6791
6792 return 0;
6793}
6794
6795static void
6796chanserv_dnr_read(const char *key, struct record_data *hir)
6797{
6798 const char *setter, *reason, *str;
6799 struct do_not_register *dnr;
6800
6801 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6802 if(!setter)
6803 {
6804 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6805 return;
6806 }
6807 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6808 if(!reason)
6809 {
6810 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6811 return;
6812 }
6813 dnr = chanserv_add_dnr(key, setter, reason);
6814 if(!dnr)
6815 return;
6816 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6817 if(str)
6818 dnr->set = atoi(str);
6819 else
6820 dnr->set = 0;
6821}
6822
6823static int
6824chanserv_saxdb_read(struct dict *database)
6825{
6826 struct dict *section;
6827 dict_iterator_t it;
6828
6829 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6830 for(it = dict_first(section); it; it = iter_next(it))
6831 chanserv_note_type_read(iter_key(it), iter_data(it));
6832
6833 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6834 for(it = dict_first(section); it; it = iter_next(it))
6835 chanserv_channel_read(iter_key(it), iter_data(it));
6836
6837 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6838 for(it = dict_first(section); it; it = iter_next(it))
6839 chanserv_dnr_read(iter_key(it), iter_data(it));
6840
6841 return 0;
6842}
6843
6844static int
6845chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6846{
6847 int high_present = 0;
6848 saxdb_start_record(ctx, KEY_USERS, 1);
6849 for(; uData; uData = uData->next)
6850 {
6851 if((uData->access >= UL_PRESENT) && uData->present)
6852 high_present = 1;
6853 saxdb_start_record(ctx, uData->handle->handle, 0);
6854 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6855 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6856 if(uData->flags)
6857 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6858 if(uData->info)
6859 saxdb_write_string(ctx, KEY_INFO, uData->info);
6860 saxdb_end_record(ctx);
6861 }
6862 saxdb_end_record(ctx);
6863 return high_present;
6864}
6865
6866static void
6867chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6868{
6869 if(!bData)
6870 return;
6871 saxdb_start_record(ctx, KEY_BANS, 1);
6872 for(; bData; bData = bData->next)
6873 {
6874 saxdb_start_record(ctx, bData->mask, 0);
6875 saxdb_write_int(ctx, KEY_SET, bData->set);
6876 if(bData->triggered)
6877 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6878 if(bData->expires)
6879 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6880 if(bData->owner[0])
6881 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6882 if(bData->reason)
6883 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6884 saxdb_end_record(ctx);
6885 }
6886 saxdb_end_record(ctx);
6887}
6888
6889static void
6890chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6891{
6892 saxdb_start_record(ctx, name, 0);
6893 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6894 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6895 if(susp->issued)
6896 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6897 if(susp->expires)
6898 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6899 if(susp->revoked)
6900 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6901 if(susp->previous)
6902 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6903 saxdb_end_record(ctx);
6904}
6905
6906static void
6907chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6908{
6909 char buf[MAXLEN];
6910 int high_present;
6911 enum levelOption lvlOpt;
6912 enum charOption chOpt;
6913
6914 saxdb_start_record(ctx, channel->channel->name, 1);
6915
6916 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6917 saxdb_write_int(ctx, KEY_MAX, channel->max);
6918 if(channel->topic)
6919 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6920 if(channel->registrar)
6921 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6922 if(channel->greeting)
6923 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6924 if(channel->user_greeting)
6925 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6926 if(channel->topic_mask)
6927 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6928 if(channel->suspended)
6929 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6930
6931 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6932 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6933 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6934 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6935 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6936 {
6937 buf[0] = channel->chOpts[chOpt];
6938 buf[1] = '\0';
6939 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6940 }
6941 saxdb_end_record(ctx);
6942
6943 if(channel->modes.modes_set || channel->modes.modes_clear)
6944 {
6945 mod_chanmode_format(&channel->modes, buf);
6946 saxdb_write_string(ctx, KEY_MODES, buf);
6947 }
6948
6949 high_present = chanserv_write_users(ctx, channel->users);
6950 chanserv_write_bans(ctx, channel->bans);
6951
6952 if(dict_size(channel->notes))
6953 {
6954 dict_iterator_t it;
6955
6956 saxdb_start_record(ctx, KEY_NOTES, 1);
6957 for(it = dict_first(channel->notes); it; it = iter_next(it))
6958 {
6959 struct note *note = iter_data(it);
6960 saxdb_start_record(ctx, iter_key(it), 0);
6961 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6962 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6963 saxdb_end_record(ctx);
6964 }
6965 saxdb_end_record(ctx);
6966 }
6967
6968 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6969 saxdb_end_record(ctx);
6970}
6971
6972static void
6973chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6974{
6975 const char *str;
6976
6977 saxdb_start_record(ctx, ntype->name, 0);
6978 switch(ntype->set_access_type)
6979 {
6980 case NOTE_SET_CHANNEL_ACCESS:
6981 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6982 break;
6983 case NOTE_SET_CHANNEL_SETTER:
6984 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6985 break;
6986 case NOTE_SET_PRIVILEGED: default:
6987 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6988 break;
6989 }
6990 switch(ntype->visible_type)
6991 {
6992 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6993 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6994 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6995 }
6996 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6997 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6998 saxdb_end_record(ctx);
6999}
7000
7001static void
7002write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7003{
7004 struct do_not_register *dnr;
7005 dict_iterator_t it;
7006
7007 for(it = dict_first(dnrs); it; it = iter_next(it))
7008 {
7009 dnr = iter_data(it);
7010 saxdb_start_record(ctx, dnr->chan_name, 0);
7011 if(dnr->set)
7012 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7013 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7014 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7015 saxdb_end_record(ctx);
7016 }
7017}
7018
7019static int
7020chanserv_saxdb_write(struct saxdb_context *ctx)
7021{
7022 dict_iterator_t it;
7023 struct chanData *channel;
7024
7025 /* Notes */
7026 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7027 for(it = dict_first(note_types); it; it = iter_next(it))
7028 chanserv_write_note_type(ctx, iter_data(it));
7029 saxdb_end_record(ctx);
7030
7031 /* DNRs */
7032 saxdb_start_record(ctx, KEY_DNR, 1);
7033 write_dnrs_helper(ctx, handle_dnrs);
7034 write_dnrs_helper(ctx, plain_dnrs);
7035 write_dnrs_helper(ctx, mask_dnrs);
7036 saxdb_end_record(ctx);
7037
7038 /* Channels */
7039 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7040 for(channel = channelList; channel; channel = channel->next)
7041 chanserv_write_channel(ctx, channel);
7042 saxdb_end_record(ctx);
7043
7044 return 0;
7045}
7046
7047static void
7048chanserv_db_cleanup(void) {
7049 unsigned int ii;
7050 unreg_part_func(handle_part);
7051 while(channelList)
7052 unregister_channel(channelList, "terminating.");
7053 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7054 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7055 free(chanserv_conf.support_channels.list);
7056 dict_delete(handle_dnrs);
7057 dict_delete(plain_dnrs);
7058 dict_delete(mask_dnrs);
7059 dict_delete(note_types);
7060 free_string_list(chanserv_conf.eightball);
7061 free_string_list(chanserv_conf.old_ban_names);
7062 free_string_list(chanserv_conf.set_shows);
7063 free(set_shows_list.list);
7064 free(uset_shows_list.list);
7065 while(helperList)
7066 {
7067 struct userData *helper = helperList;
7068 helperList = helperList->next;
7069 free(helper);
7070 }
7071}
7072
7073#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7074#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7075#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7076
7077void
7078init_chanserv(const char *nick)
7079{
7080 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7081 conf_register_reload(chanserv_conf_read);
7082
7083 reg_server_link_func(handle_server_link);
7084
7085 reg_new_channel_func(handle_new_channel);
7086 reg_join_func(handle_join);
7087 reg_part_func(handle_part);
7088 reg_kick_func(handle_kick);
7089 reg_topic_func(handle_topic);
7090 reg_mode_change_func(handle_mode);
7091 reg_nick_change_func(handle_nick_change);
7092
7093 reg_auth_func(handle_auth);
7094 reg_handle_rename_func(handle_rename);
7095 reg_unreg_func(handle_unreg);
7096
7097 handle_dnrs = dict_new();
7098 dict_set_free_data(handle_dnrs, free);
7099 plain_dnrs = dict_new();
7100 dict_set_free_data(plain_dnrs, free);
7101 mask_dnrs = dict_new();
7102 dict_set_free_data(mask_dnrs, free);
7103
7104 reg_svccmd_unbind_func(handle_svccmd_unbind);
7105 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7106 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7107 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7108 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7109 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7110 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7111 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7112 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7113 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7114
7115 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7116 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7117
4048352e 7118 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
7119 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
7120 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
7121 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 7122 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7123
7124 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7125 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
4048352e 7126 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7127 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
7128 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 7129
4048352e 7130 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 7131 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
4048352e 7132 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 7133 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7134
7135 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7136 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7137 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7138 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7139 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7140 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7141 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7142 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7143
7144 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7145 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7146 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7147 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7148 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7149 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7150 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7151 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7152 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7153 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
4048352e 7154 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
d76ed9a9 7155 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
4048352e 7156 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
7157 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 7158
7159 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7160 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7161 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7162 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7163 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7164
7165 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7166 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7167
7168 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7169 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7170 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7171 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7172 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7173 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7174 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7175 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7176 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7177 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7178 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7179
7180 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7181 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7182
7183 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7184 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7185 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7186 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7187
7188 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7189 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7190 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7191 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7192 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7193
7194 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7195 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7196 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7197 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7198 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7199 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7200
7201 /* Channel options */
7202 DEFINE_CHANNEL_OPTION(defaulttopic);
7203 DEFINE_CHANNEL_OPTION(topicmask);
7204 DEFINE_CHANNEL_OPTION(greeting);
7205 DEFINE_CHANNEL_OPTION(usergreeting);
7206 DEFINE_CHANNEL_OPTION(modes);
7207 DEFINE_CHANNEL_OPTION(enfops);
7208 DEFINE_CHANNEL_OPTION(giveops);
7209 DEFINE_CHANNEL_OPTION(protect);
7210 DEFINE_CHANNEL_OPTION(enfmodes);
7211 DEFINE_CHANNEL_OPTION(enftopic);
7212 DEFINE_CHANNEL_OPTION(pubcmd);
7213 DEFINE_CHANNEL_OPTION(givevoice);
7214 DEFINE_CHANNEL_OPTION(userinfo);
7215 DEFINE_CHANNEL_OPTION(dynlimit);
7216 DEFINE_CHANNEL_OPTION(topicsnarf);
7217 DEFINE_CHANNEL_OPTION(nodelete);
7218 DEFINE_CHANNEL_OPTION(toys);
7219 DEFINE_CHANNEL_OPTION(setters);
7220 DEFINE_CHANNEL_OPTION(topicrefresh);
7221 DEFINE_CHANNEL_OPTION(ctcpusers);
7222 DEFINE_CHANNEL_OPTION(ctcpreaction);
7223 DEFINE_CHANNEL_OPTION(inviteme);
7224 if(off_channel)
7225 DEFINE_CHANNEL_OPTION(offchannel);
7226 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7227
7228 /* Alias set topic to set defaulttopic for compatibility. */
7229 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7230
7231 /* User options */
7232 DEFINE_USER_OPTION(noautoop);
7233 DEFINE_USER_OPTION(autoinvite);
7234 DEFINE_USER_OPTION(info);
7235
7236 /* Alias uset autovoice to uset autoop. */
7237 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7238
7239 note_types = dict_new();
7240 dict_set_free_data(note_types, chanserv_deref_note_type);
7241 if(nick)
7242 {
7243 chanserv = AddService(nick, "Channel Services", NULL);
7244 service_register(chanserv)->trigger = '!';
7245 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7246 }
7247 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7248
7249 if(chanserv_conf.channel_expire_frequency)
7250 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7251
7252 if(chanserv_conf.refresh_period)
7253 {
7254 time_t next_refresh;
7255 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7256 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7257 }
7258
7259 reg_exit_func(chanserv_db_cleanup);
7260 message_register_table(msgtab);
7261}