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