]> jfr.im git - irc/evilnet/x3.git/blame - src/chanserv.c
Added missing PRIV_* flags
[irc/evilnet/x3.git] / src / chanserv.c
CommitLineData
d76ed9a9
AS
1/* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9
AS
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"
b404335b 24#include "gline.h"
25#include "ioset.h"
d76ed9a9
AS
26#include "modcmd.h"
27#include "opserv.h" /* for opserv_bad_channel() */
28#include "saxdb.h"
b404335b 29#include "shun.h"
63c95a47 30#include "spamserv.h"
d76ed9a9
AS
31#include "timeq.h"
32
33#define CHANSERV_CONF_NAME "services/chanserv"
34
35/* ChanServ options */
36#define KEY_SUPPORT_CHANNEL "support_channel"
37#define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
38#define KEY_DB_BACKUP_FREQ "db_backup_freq"
39#define KEY_INFO_DELAY "info_delay"
40#define KEY_MAX_GREETLEN "max_greetlen"
41#define KEY_ADJUST_THRESHOLD "adjust_threshold"
42#define KEY_ADJUST_DELAY "adjust_delay"
43#define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
44#define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
31f23f13 45#define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
d76ed9a9
AS
46#define KEY_MAX_CHAN_USERS "max_chan_users"
47#define KEY_MAX_CHAN_BANS "max_chan_bans"
48#define KEY_NICK "nick"
49#define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
d76ed9a9
AS
50#define KEY_8BALL_RESPONSES "8ball"
51#define KEY_OLD_BAN_NAMES "old_ban_names"
52#define KEY_REFRESH_PERIOD "refresh_period"
53#define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
54#define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
55#define KEY_MAX_OWNED "max_owned"
56#define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
57#define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
58#define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
59#define KEY_NODELETE_LEVEL "nodelete_level"
60#define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
a32da4c7 61#define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
d76ed9a9
AS
62
63/* ChanServ database */
c8ca69a0 64#define KEY_VERSION_CONTROL "version_control"
d76ed9a9
AS
65#define KEY_CHANNELS "channels"
66#define KEY_NOTE_TYPES "note_types"
67
c8ca69a0
AS
68/* version control paramiter */
69#define KEY_VERSION_NUMBER "version_number"
70
d76ed9a9
AS
71/* Note type parameters */
72#define KEY_NOTE_OPSERV_ACCESS "opserv_access"
73#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
74#define KEY_NOTE_SETTER_ACCESS "setter_access"
75#define KEY_NOTE_VISIBILITY "visibility"
76#define KEY_NOTE_VIS_PRIVILEGED "privileged"
77#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
78#define KEY_NOTE_VIS_ALL "all"
79#define KEY_NOTE_MAX_LENGTH "max_length"
80#define KEY_NOTE_SETTER "setter"
81#define KEY_NOTE_NOTE "note"
82
83/* Do-not-register channels */
84#define KEY_DNR "dnr"
85#define KEY_DNR_SET "set"
86#define KEY_DNR_SETTER "setter"
87#define KEY_DNR_REASON "reason"
88
89/* Channel data */
90#define KEY_REGISTERED "registered"
91#define KEY_REGISTRAR "registrar"
92#define KEY_SUSPENDED "suspended"
93#define KEY_PREVIOUS "previous"
94#define KEY_SUSPENDER "suspender"
95#define KEY_ISSUED "issued"
96#define KEY_REVOKED "revoked"
97#define KEY_SUSPEND_EXPIRES "suspend_expires"
98#define KEY_SUSPEND_REASON "suspend_reason"
82f37c08 99#define KEY_GIVEOWNERSHIP "giveownership"
100#define KEY_STAFF_ISSUER "staff_issuer"
101#define KEY_OLD_OWNER "old_owner"
102#define KEY_TARGET "target"
103#define KEY_TARGET_ACCESS "target_access"
d76ed9a9
AS
104#define KEY_VISITED "visited"
105#define KEY_TOPIC "topic"
106#define KEY_GREETING "greeting"
107#define KEY_USER_GREETING "user_greeting"
108#define KEY_MODES "modes"
109#define KEY_FLAGS "flags"
110#define KEY_OPTIONS "options"
111#define KEY_USERS "users"
c8273589 112#define KEY_BANS "bans" /* for lamers */
d76ed9a9
AS
113#define KEY_MAX "max"
114#define KEY_NOTES "notes"
115#define KEY_TOPIC_MASK "topic_mask"
a32da4c7 116#define KEY_OWNER_TRANSFER "owner_transfer"
8b9e7d45 117#define KEY_MAXSETINFO "maxsetinfo"
d76ed9a9
AS
118
119/* User data */
120#define KEY_LEVEL "level"
121#define KEY_INFO "info"
122#define KEY_SEEN "seen"
dfaa28a4 123#define KEY_ACCESSEXPIRY "accessexpiry"
124#define KEY_CLVLEXPIRY "clvlexpiry"
125#define KEY_LASTLEVEL "lastlevel"
d76ed9a9
AS
126
127/* Ban data */
128#define KEY_OWNER "owner"
129#define KEY_REASON "reason"
130#define KEY_SET "set"
131#define KEY_DURATION "duration"
132#define KEY_EXPIRES "expires"
133#define KEY_TRIGGERED "triggered"
134
7637f48f 135#define KEY_GOD_TIMEOUT "god_timeout"
136
0f6fe38c 137#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
d76ed9a9
AS
138#define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
139
140/* Administrative messages */
141static const struct message_entry msgtab[] = {
142 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
143
144/* Channel registration */
145 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
146 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
147 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
f8f30e75 148 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
d76ed9a9 149 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
f8f30e75
AS
150 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
151 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
152 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
d76ed9a9
AS
153
154/* Do-not-register channels */
155 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
de9510bc
AS
156 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
157 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
158 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
d76ed9a9
AS
159 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
160 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
161 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
162 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
163 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
164 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
165 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
166
167/* Channel unregistration */
168 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
169 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
170 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
fa0fac3e 171 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
d76ed9a9
AS
172
173/* Channel moving */
174 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
175 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
176
177/* Channel merging */
178 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
179 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
180 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
181 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
182 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
183
184/* Handle unregistration */
185 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
186
187/* Error messages */
188 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
189 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
190 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
191 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
c8273589 192 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
d76ed9a9
AS
193 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
194 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
195 { "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." },
196 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
55342ce8 197 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
d76ed9a9 198 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
55342ce8 199 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
d76ed9a9
AS
200 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
201 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
202
203/* Removing yourself from a channel. */
204 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
205 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
206 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
207
208/* User management */
fd20b142 209 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
dfaa28a4 210 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
de9510bc 211 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
d76ed9a9
AS
212 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
213 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
214 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
215 { "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
AS
216 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
217 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
5ca901c3 218 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
ac3bdc8d 219 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
1d957482
AS
220 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
221 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
1bcd5f19 222 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
ac3bdc8d 223 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
1bcd5f19 224 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
d76ed9a9
AS
225 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
226
227 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
228 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
dfaa28a4 229 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
d76ed9a9 230 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
5678501c 231 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
a32da4c7 232 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
d76ed9a9
AS
233 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
234 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
235
236/* Ban management */
c8273589
AS
237 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
238 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
d76ed9a9
AS
239 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
240 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
c8273589
AS
241 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
242 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
9a8aa132 243 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
c8273589 244 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
d76ed9a9 245 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
c8273589 246 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
d76ed9a9
AS
247 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
248 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
249 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
250 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
251 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
c8273589 252 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
d76ed9a9
AS
253 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
254
255 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
256
257/* Channel management */
258 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
259 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
260 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
261
262 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
263 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
264 { "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 265 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
d76ed9a9
AS
266 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
267 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
268 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
269
270 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
271 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
272 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
273 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
274 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
275 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
276 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
277 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
278 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
d76ed9a9
AS
279 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
280 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
281 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
282 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
283 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
284 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
285 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
286 { "CSMSG_SET_MODES", "$bModes $b %s" },
287 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
35caf917 288 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
d76ed9a9 289 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
35caf917 290 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
d76ed9a9 291 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
35caf917
AS
292 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
293 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
294 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
35caf917
AS
295 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
296 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
297 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
298 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
4b6129c0 299 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
d76ed9a9
AS
300 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
301 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
302 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
303 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
7637f48f 304 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
31f23f13 305 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
8b9e7d45 306 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
4b6129c0 307
c8ca69a0
AS
308 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
309 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
d76ed9a9 310 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
cd25f2e9 311 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
d76ed9a9
AS
312 { "CSMSG_USET_INFO", "$bInfo $b %s" },
313
314 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
31f23f13 315 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
d76ed9a9 316 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
55342ce8 317 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
d76ed9a9
AS
318 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
319 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
55342ce8 320 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
d76ed9a9 321 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
55342ce8 322 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
d76ed9a9
AS
323 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
324 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
4b6129c0
AS
325
326 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
327 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
639bdb1d
AS
328 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
329 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
330 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
331 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
4d69a3b1 332 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
4b6129c0 333
d76ed9a9
AS
334 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
335 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
336 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
337 { "CSMSG_PROTECT_NONE", "No users will be protected." },
338 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
339 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
340 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
31f23f13 341
d76ed9a9
AS
342 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
343 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
344 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
345 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
346 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
31f23f13 347
7637f48f 348 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
349 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
350 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
351 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
352 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
353
35caf917 354 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
d76ed9a9
AS
355 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
356 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
357 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
358 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
31f23f13
AS
359
360 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
361 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
362 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
363 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
364 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
365 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
d76ed9a9
AS
366
367 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
368 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
369 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
5f1600ab 370 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
d76ed9a9
AS
371 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
372 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
373 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
374 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
375 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
376
377 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
c8273589 378 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
d76ed9a9
AS
379 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
380
381/* Channel userlist */
338a82b5
AS
382 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
383 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
384 /* uncomment if needed to adujust styles (and change code below)
385 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
386 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
387 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
388 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
d9896a83
AS
389 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
390 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
338a82b5 391 */
d76ed9a9 392 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
de9510bc 393 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
c8273589 394 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
d76ed9a9
AS
395
396/* Channel note list */
397 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
398 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
399 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
400 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
401 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
402 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
403 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
404 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
405 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
406 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
407 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
408 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
409 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
410 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
411 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
412
413/* Channel [un]suspension */
414 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
415 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
416 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
417 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
418 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
419 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
420 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
421
422/* Access information */
423 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
424 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
425 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
426 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
427 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
de9510bc 428 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
d76ed9a9 429 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
de9510bc 430 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
d76ed9a9
AS
431 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
432 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
433 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
434
435/* Seen information */
436 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
437 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
438 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
439 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
440
441/* Names information */
442 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
443 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
444
445/* Channel information */
de9510bc
AS
446 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
447 { "CSMSG_BAR", "----------------------------------------"},
d76ed9a9
AS
448 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
449 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
450 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
451 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
c8273589
AS
452 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
453 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
d76ed9a9
AS
454 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
455 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
456 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
457 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
458 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
459 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
460 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
461 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
462 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
463 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
464 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
465 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
466 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
467 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
82f37c08 468 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
469 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
470 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
471 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
de9510bc 472 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
cde20a3f 473 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
d76ed9a9 474
de9510bc 475 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
d76ed9a9
AS
476 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
477 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
478 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
479 { "CSMSG_PEEK_OPS", "$bOps:$b" },
480 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
de9510bc 481 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
d76ed9a9
AS
482
483/* Network information */
484 { "CSMSG_NETWORK_INFO", "Network Information:" },
485 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
486 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
c8273589 487 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
d76ed9a9
AS
488 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
489 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
490 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
491 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
492 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
493
494/* Staff list */
495 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
496 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
497 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
498
499/* Channel searches */
500 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
501 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
502 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
de9510bc 503 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
d76ed9a9
AS
504
505/* Channel configuration */
506 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
de9510bc
AS
507 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
508 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
d76ed9a9
AS
509 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
510
511/* User settings */
512 { "CSMSG_USER_OPTIONS", "User Options:" },
35caf917 513// { "CSMSG_USER_PROTECTED", "That user is protected." },
d76ed9a9
AS
514
515/* Toys */
516 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
517 { "CSMSG_PING_RESPONSE", "Pong!" },
518 { "CSMSG_WUT_RESPONSE", "wut" },
519 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
520 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
521 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
522 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
523 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
524 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
525 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
87708af4 526 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
b404335b 527 { "CSMSG_ROULETTE_NEW", "Please type .roulette to start a new round" } ,
87708af4 528 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
529 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
530 { "CSMSG_ROULETTE_CLICK", "Click" } ,
d76ed9a9 531
b404335b 532 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
533 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
534 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
535
536 { "CSMSG_SPIN_1", "1: Peer's gonna eat you!!!!" } ,
537 { "CSMSG_SPIN_2", "2: Part all channels" } ,
538 { "CSMSG_SPIN_3", "3: Part all channels" } ,
539 { "CSMSG_SPIN_4", "4: /gline for random amount of time" } ,
540 { "CSMSG_SPIN_5", "5: /shun for random amount of time" } ,
541 { "CSMSG_SPIN_6", "6: Absolutely nothing" } ,
542 { "CSMSG_SPIN_7", "7: Join a bunch of random channels, then /part all of 'em several times" } ,
543 { "CSMSG_SPIN_8", "8: Abuse line added to /whois info" } ,
544 { "CSMSG_SPIN_9", "9: /kick from each channel you're in" } ,
545 { "CSMSG_SPIN_10", "10: Random Nick Change" } ,
546 { "CSMSG_SPIN_11", "11: /kill" } ,
547 { "CSMSG_SPIN_12", "12: Services ignore for random amount of time" } ,
548 { "CSMSG_SPIN_13", "13: /kick and ban from each channel your're in" } ,
549
d76ed9a9 550/* Other things */
de9510bc 551 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
23475fc6 552 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
08895577 553 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
554 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
d76ed9a9
AS
555 { NULL, NULL }
556};
557
558/* eject_user and unban_user flags */
559#define ACTION_KICK 0x0001
560#define ACTION_BAN 0x0002
c8273589
AS
561#define ACTION_ADD_LAMER 0x0004
562#define ACTION_ADD_TIMED_LAMER 0x0008
d76ed9a9 563#define ACTION_UNBAN 0x0010
c8273589 564#define ACTION_DEL_LAMER 0x0020
d76ed9a9
AS
565
566/* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
567#define MODELEN 40 + KEYLEN
568#define PADLEN 21
569#define ACCESSLEN 10
570
571#define CSFUNC_ARGS user, channel, argc, argv, cmd
572
573#define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
567a5f26 574#define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
d76ed9a9
AS
575#define REQUIRE_PARAMS(N) if(argc < (N)) { \
576 reply("MSG_MISSING_PARAMS", argv[0]); \
577 CHANSERV_SYNTAX(); \
578 return 0; }
579
580DECLARE_LIST(dnrList, struct do_not_register *);
581DEFINE_LIST(dnrList, struct do_not_register *);
582
583static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
584
585struct userNode *chanserv;
586dict_t note_types;
587int off_channel;
7637f48f 588extern struct string_list *autojoin_channels;
d76ed9a9
AS
589static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
590static struct log_type *CS_LOG;
ac3bdc8d
AS
591struct adduserPending* adduser_pendings = NULL;
592unsigned int adduser_pendings_count = 0;
7637f48f 593unsigned long god_timeout;
d76ed9a9
AS
594
595static struct
596{
597 struct channelList support_channels;
598 struct mod_chanmode default_modes;
599
600 unsigned long db_backup_frequency;
601 unsigned long channel_expire_frequency;
31f23f13 602 unsigned long ban_timeout_frequency;
d76ed9a9
AS
603
604 long info_delay;
605 unsigned int adjust_delay;
606 long channel_expire_delay;
607 unsigned int nodelete_level;
608
609 unsigned int adjust_threshold;
610 int join_flood_threshold;
611
612 unsigned int greeting_length;
613 unsigned int refresh_period;
a32da4c7 614 unsigned int giveownership_period;
d76ed9a9
AS
615
616 unsigned int max_owned;
617 unsigned int max_chan_users;
c8273589 618 unsigned int max_chan_bans; /* lamers */
d76ed9a9
AS
619 unsigned int max_userinfo_length;
620
621 struct string_list *set_shows;
622 struct string_list *eightball;
623 struct string_list *old_ban_names;
624
625 const char *ctcp_short_ban_duration;
626 const char *ctcp_long_ban_duration;
627
628 const char *irc_operator_epithet;
629 const char *network_helper_epithet;
630 const char *support_helper_epithet;
631} chanserv_conf;
632
633struct listData
634{
635 struct userNode *user;
636 struct userNode *bot;
637 struct chanNode *channel;
638 const char *search;
639 unsigned short lowest;
640 unsigned short highest;
641 struct userData **users;
642 struct helpfile_table table;
643};
644
645enum note_access_type
646{
647 NOTE_SET_CHANNEL_ACCESS,
648 NOTE_SET_CHANNEL_SETTER,
649 NOTE_SET_PRIVILEGED
650};
651
652enum note_visible_type
653{
654 NOTE_VIS_ALL,
655 NOTE_VIS_CHANNEL_USERS,
656 NOTE_VIS_PRIVILEGED
657};
658
b404335b 659struct io_fd *socket_io_fd;
660extern struct cManagerNode cManager;
661
d76ed9a9
AS
662struct note_type
663{
664 enum note_access_type set_access_type;
665 union {
666 unsigned int min_opserv;
667 unsigned short min_ulevel;
668 } set_access;
669 enum note_visible_type visible_type;
670 unsigned int max_length;
671 unsigned int refs;
672 char name[1];
673};
674
675struct note
676{
677 struct note_type *type;
678 char setter[NICKSERV_HANDLE_LEN+1];
679 char note[1];
680};
681
682static unsigned int registered_channels;
683static unsigned int banCount;
684
685static const struct {
686 char *name;
687 char *title;
688 unsigned short level;
689 char ch;
a915f7d4 690} accessLevels[] = { /* MUST be orderd less to most! */
d76ed9a9 691 { "peon", "Peon", UL_PEON, '+' },
55342ce8 692 { "halfop", "HalfOp", UL_HALFOP, '%' },
d76ed9a9 693 { "op", "Op", UL_OP, '@' },
4048352e 694 { "manager", "Manager", UL_MANAGER, '%' },
d76ed9a9
AS
695 { "coowner", "Coowner", UL_COOWNER, '*' },
696 { "owner", "Owner", UL_OWNER, '!' },
697 { "helper", "BUG:", UL_HELPER, 'X' }
698};
699
35caf917 700/* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
d76ed9a9
AS
701static const struct {
702 char *format_name;
703 char *db_name;
704 unsigned short default_value;
705 unsigned int old_idx;
706 unsigned int old_flag;
707 unsigned short flag_value;
708} levelOptions[] = {
e3b2f789
AS
709 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
710 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
711 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
712 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
713 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
714 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
715 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
716 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
d76ed9a9
AS
717 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
718};
719
720struct charOptionValues {
721 char value;
722 char *format_name;
4b6129c0
AS
723} automodeValues[] = {
724 { 'n', "CSMSG_AUTOMODE_NONE" },
725 { 'y', "CSMSG_AUTOMODE_NORMAL" },
726 { 'v', "CSMSG_AUTOMODE_VOICE" },
727 { 'h', "CSMSG_AUTOMODE_HOP" },
728 { 'o', "CSMSG_AUTOMODE_OP" },
4d69a3b1
AS
729 { 'm', "CSMSG_AUTOMODE_MUTE" },
730 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
c8ca69a0 731}, protectValues[] = {
d76ed9a9
AS
732 { 'a', "CSMSG_PROTECT_ALL" },
733 { 'e', "CSMSG_PROTECT_EQUAL" },
734 { 'l', "CSMSG_PROTECT_LOWER" },
735 { 'n', "CSMSG_PROTECT_NONE" }
736}, toysValues[] = {
737 { 'd', "CSMSG_TOYS_DISABLED" },
738 { 'n', "CSMSG_TOYS_PRIVATE" },
739 { 'p', "CSMSG_TOYS_PUBLIC" }
740}, topicRefreshValues[] = {
741 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
742 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
743 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
744 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
745 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
746}, ctcpReactionValues[] = {
35caf917 747 { 'n', "CSMSG_CTCPREACTION_NONE" },
d76ed9a9
AS
748 { 'k', "CSMSG_CTCPREACTION_KICK" },
749 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
750 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
751 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
31f23f13
AS
752}, banTimeoutValues[] = {
753 { '0', "CSMSG_BANTIMEOUT_NONE" },
754 { '1', "CSMSG_BANTIMEOUT_10M" },
755 { '2', "CSMSG_BANTIMEOUT_2H" },
756 { '3', "CSMSG_BANTIMEOUT_4H" },
757 { '4', "CSMSG_BANTIMEOUT_1D" },
758 { '5', "CSMSG_BANTIMEOUT_1W" }
d3347099
AS
759},
760resyncValues[] = {
7637f48f 761 { 'n', "CSMSG_RESYNC_NEVER" },
762 { '1', "CSMSG_RESYNC_3_HOURS" },
763 { '2', "CSMSG_RESYNC_6_HOURS" },
764 { '3', "CSMSG_RESYNC_12_HOURS" },
765 { '4', "CSMSG_RESYNC_24_HOURS" }
d76ed9a9
AS
766};
767
768static const struct {
769 char *format_name;
770 char *db_name;
771 char default_value;
772 unsigned int old_idx;
773 unsigned char count;
774 struct charOptionValues *values;
775} charOptions[] = {
4b6129c0 776 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues), automodeValues },
c8ca69a0
AS
777 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
778 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
d76ed9a9 779 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
31f23f13 780 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
7637f48f 781 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues },
782 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues), resyncValues },
d76ed9a9
AS
783};
784
785struct userData *helperList;
786struct chanData *channelList;
787static struct module *chanserv_module;
788static unsigned int userCount;
c8ca69a0
AS
789unsigned int chanserv_read_version = 0; /* db version control */
790
791#define CHANSERV_DB_VERSION 2
d76ed9a9 792
d76ed9a9
AS
793#define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
794#define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
795
b404335b 796void sputsock(const char *text, ...) PRINTF_LIKE(1, 2);
797
798void
799sputsock(const char *text, ...)
800{
801 va_list arg_list;
802 char buffer[MAXLEN];
803 int pos;
804
805 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
806 buffer[0] = '\0';
807 va_start(arg_list, text);
808 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
809 va_end(arg_list);
810 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
811 buffer[pos] = 0;
812 log_replay(MAIN_LOG, true, buffer);
813 buffer[pos++] = '\n';
814 buffer[pos] = 0;
815 ioset_write(socket_io_fd, buffer, pos);
816}
817
d76ed9a9
AS
818unsigned short
819user_level_from_name(const char *name, unsigned short clamp_level)
820{
821 unsigned int level = 0, ii;
822 if(isdigit(name[0]))
823 level = strtoul(name, NULL, 10);
824 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
825 if(!irccasecmp(name, accessLevels[ii].name))
826 level = accessLevels[ii].level;
827 if(level > clamp_level)
828 return 0;
829 return level;
830}
831
e6892204
AS
832char *
833user_level_name_from_level(int level)
834{
835 unsigned int ii;
a915f7d4
AS
836 char* highest;
837
838 highest = "None";
839 if(level >= 1)
840 highest = "Peon";
e6892204 841 for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
a915f7d4
AS
842 if(level >= accessLevels[ii].level)
843 highest = accessLevels[ii].title;
844 return(highest);
e6892204
AS
845}
846
847
d76ed9a9
AS
848int
849parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
850{
851 char *sep;
852 *minl = strtoul(arg, &sep, 10);
853 if(*sep == '\0')
854 {
855 *maxl = *minl;
856 return 1;
857 }
858 else if(*sep == '-')
859 {
860 *maxl = strtoul(sep+1, &sep, 10);
861 return *sep == '\0';
862 }
863 else
864 return 0;
865}
866
867struct userData*
868_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
869{
870 struct userData *uData, **head;
871
872 if(!channel || !handle)
873 return NULL;
874
875 if(override && HANDLE_FLAGGED(handle, HELPING)
876 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
877 {
878 for(uData = helperList;
879 uData && uData->handle != handle;
880 uData = uData->next);
881
882 if(!uData)
883 {
884 uData = calloc(1, sizeof(struct userData));
885 uData->handle = handle;
886
887 uData->access = UL_HELPER;
888 uData->seen = 0;
889
890 uData->info = NULL;
891
892 uData->prev = NULL;
893 uData->next = helperList;
894 if(helperList)
895 helperList->prev = uData;
896 helperList = uData;
897 }
898
899 head = &helperList;
900 }
901 else
902 {
903 for(uData = channel->users; uData; uData = uData->next)
904 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
905 break;
906
907 head = &(channel->users);
908 }
909
910 if(uData && (uData != *head))
911 {
912 /* Shuffle the user to the head of whatever list he was in. */
913 if(uData->next)
914 uData->next->prev = uData->prev;
915 if(uData->prev)
916 uData->prev->next = uData->next;
917
918 uData->prev = NULL;
919 uData->next = *head;
920
921 if(*head)
922 (**head).prev = uData;
923 *head = uData;
924 }
925
926 return uData;
927}
928
929/* Returns non-zero if user has at least the minimum access.
930 * exempt_owner is set when handling !set, so the owner can set things
931 * to/from >500.
932 */
933int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
934{
935 struct userData *uData;
936 struct chanData *cData = channel->channel_info;
937 unsigned short minimum = cData->lvlOpts[opt];
938 if(!minimum)
939 return 1;
940 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
941 if(!uData)
942 return 0;
943 if(minimum <= uData->access)
944 return 1;
945 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
946 return 1;
947 return 0;
948}
949
950/* Scan for other users authenticated to the same handle
951 still in the channel. If so, keep them listed as present.
952
953 user is optional, if not null, it skips checking that userNode
954 (for the handle_part function) */
955static void
956scan_user_presence(struct userData *uData, struct userNode *user)
957{
958 struct modeNode *mn;
959
960 if(IsSuspended(uData->channel)
961 || IsUserSuspended(uData)
962 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
963 {
964 uData->present = 0;
965 }
966 else
967 {
968 uData->present = 1;
969 uData->seen = now;
970 }
971}
972
973static void
0f6fe38c 974chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
d76ed9a9
AS
975{
976 unsigned int eflags, argc;
977 char *argv[4];
978 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
979
980 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
981 if(!channel->channel_info
982 || IsSuspended(channel->channel_info)
983 || IsService(user)
984 || !ircncasecmp(text, "ACTION ", 7))
985 return;
35caf917
AS
986 /* We dont punish people we know -Rubin
987 * * Figure out the minimum level needed to CTCP the channel *
988 *
989 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
990 * return;
991 */
992 /* If they are a user of the channel, they are exempt */
993 if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
994 return;
d76ed9a9
AS
995 /* We need to enforce against them; do so. */
996 eflags = 0;
0f6fe38c 997 argv[0] = text;
d76ed9a9
AS
998 argv[1] = user->nick;
999 argc = 2;
1000 if(GetUserMode(channel, user))
1001 eflags |= ACTION_KICK;
1002 switch(channel->channel_info->chOpts[chCTCPReaction]) {
35caf917
AS
1003 default: case 'n': return;
1004 case 'k':
1005 eflags |= ACTION_KICK;
1006 break;
d76ed9a9
AS
1007 case 'b':
1008 eflags |= ACTION_BAN;
1009 break;
1010 case 't':
c8273589 1011 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
d76ed9a9
AS
1012 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
1013 break;
1014 case 'T':
c8273589 1015 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
d76ed9a9
AS
1016 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
1017 break;
1018 }
1019 argv[argc++] = bad_ctcp_reason;
1020 eject_user(chanserv, channel, argc, argv, NULL, eflags);
1021}
1022
1023struct note_type *
1024chanserv_create_note_type(const char *name)
1025{
1026 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
1027 strcpy(ntype->name, name);
1028 ntype->refs = 1;
1029 dict_insert(note_types, ntype->name, ntype);
1030 return ntype;
1031}
1032
1033static void
1034chanserv_deref_note_type(void *data)
1035{
1036 struct note_type *ntype = data;
1037
1038 if(--ntype->refs > 0)
1039 return;
1040 free(ntype);
1041}
1042
1043static void
1044chanserv_flush_note_type(struct note_type *ntype)
1045{
1046 struct chanData *cData;
1047 for(cData = channelList; cData; cData = cData->next)
1048 dict_remove(cData->notes, ntype->name);
1049}
1050
1051static void
1052chanserv_truncate_notes(struct note_type *ntype)
1053{
1054 struct chanData *cData;
1055 struct note *note;
1056 unsigned int size = sizeof(*note) + ntype->max_length;
1057
1058 for(cData = channelList; cData; cData = cData->next) {
1059 note = dict_find(cData->notes, ntype->name, NULL);
1060 if(!note)
1061 continue;
1062 if(strlen(note->note) <= ntype->max_length)
1063 continue;
1064 dict_remove2(cData->notes, ntype->name, 1);
1065 note = realloc(note, size);
1066 note->note[ntype->max_length] = 0;
1067 dict_insert(cData->notes, ntype->name, note);
1068 }
1069}
1070
1071static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
1072
1073static struct note *
1074chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
1075{
1076 struct note *note;
1077 unsigned int len = strlen(text);
1078
1079 if(len > type->max_length) len = type->max_length;
1080 note = calloc(1, sizeof(*note) + len);
1081 note->type = type;
1082 strncpy(note->setter, setter, sizeof(note->setter)-1);
1083 memcpy(note->note, text, len);
1084 note->note[len] = 0;
1085 dict_insert(channel->notes, type->name, note);
1086 type->refs++;
1087 return note;
1088}
1089
1090static void
1091chanserv_free_note(void *data)
1092{
1093 struct note *note = data;
1094
1095 chanserv_deref_note_type(note->type);
1096 assert(note->type->refs > 0); /* must use delnote to remove the type */
1097 free(note);
1098}
1099
1100static MODCMD_FUNC(cmd_createnote) {
1101 struct note_type *ntype;
1102 unsigned int arg = 1, existed = 0, max_length;
1103
1104 if((ntype = dict_find(note_types, argv[1], NULL)))
1105 existed = 1;
1106 else
1107 ntype = chanserv_create_note_type(argv[arg]);
1108 if(!irccasecmp(argv[++arg], "privileged"))
1109 {
1110 arg++;
1111 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1112 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1113 }
1114 else if(!irccasecmp(argv[arg], "channel"))
1115 {
1116 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1117 if(!ulvl)
1118 {
1119 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1120 goto fail;
1121 }
1122 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1123 ntype->set_access.min_ulevel = ulvl;
1124 }
1125 else if(!irccasecmp(argv[arg], "setter"))
1126 {
1127 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1128 }
1129 else
1130 {
1131 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1132 goto fail;
1133 }
1134
1135 if(!irccasecmp(argv[++arg], "privileged"))
1136 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1137 else if(!irccasecmp(argv[arg], "channel_users"))
1138 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1139 else if(!irccasecmp(argv[arg], "all"))
1140 ntype->visible_type = NOTE_VIS_ALL;
1141 else {
1142 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1143 goto fail;
1144 }
1145
1146 if((arg+1) >= argc) {
1147 reply("MSG_MISSING_PARAMS", argv[0]);
1148 goto fail;
1149 }
1150 max_length = strtoul(argv[++arg], NULL, 0);
1151 if(max_length < 20 || max_length > 450)
1152 {
1153 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1154 goto fail;
1155 }
1156 if(existed && (max_length < ntype->max_length))
1157 {
1158 ntype->max_length = max_length;
1159 chanserv_truncate_notes(ntype);
1160 }
1161 ntype->max_length = max_length;
1162
1163 if(existed)
1164 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1165 else
1166 reply("CSMSG_NOTE_CREATED", ntype->name);
1167 return 1;
1168
1169fail:
1170 if(!existed)
1171 dict_remove(note_types, ntype->name);
1172 return 0;
1173}
1174
1175static MODCMD_FUNC(cmd_removenote) {
1176 struct note_type *ntype;
1177 int force;
1178
1179 ntype = dict_find(note_types, argv[1], NULL);
1180 force = (argc > 2) && !irccasecmp(argv[2], "force");
1181 if(!ntype)
1182 {
1183 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1184 return 0;
1185 }
1186 if(ntype->refs > 1)
1187 {
1188 if(!force)
1189 {
1190 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1191 return 0;
1192 }
1193 chanserv_flush_note_type(ntype);
1194 }
1195 dict_remove(note_types, argv[1]);
1196 reply("CSMSG_NOTE_DELETED", argv[1]);
1197 return 1;
1198}
1199
1200static int
1201mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1202{
1203 if(!orig)
1204 return 0;
1205 if(orig->modes_set & change->modes_clear)
1206 return 1;
1207 if(orig->modes_clear & change->modes_set)
1208 return 1;
1209 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1210 && strcmp(orig->new_key, change->new_key))
1211 return 1;
1212 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1213 && (orig->new_limit != change->new_limit))
1214 return 1;
1215 return 0;
1216}
1217
1218static char max_length_text[MAXLEN+1][16];
1219
1220static struct helpfile_expansion
1221chanserv_expand_variable(const char *variable)
1222{
1223 struct helpfile_expansion exp;
1224
1225 if(!irccasecmp(variable, "notes"))
1226 {
1227 dict_iterator_t it;
1228 exp.type = HF_TABLE;
1229 exp.value.table.length = 1;
1230 exp.value.table.width = 3;
1231 exp.value.table.flags = 0;
1232 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1233 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1234 exp.value.table.contents[0][0] = "Note Type";
1235 exp.value.table.contents[0][1] = "Visibility";
1236 exp.value.table.contents[0][2] = "Max Length";
1237 for(it=dict_first(note_types); it; it=iter_next(it))
1238 {
1239 struct note_type *ntype = iter_data(it);
1240 int row;
1241
1242 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1243 row = exp.value.table.length++;
1244 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1245 exp.value.table.contents[row][0] = ntype->name;
1246 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1247 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1248 "unknown";
1249 if(!max_length_text[ntype->max_length][0])
1250 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1251 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1252 }
1253 return exp;
1254 }
1255
1256 exp.type = HF_STRING;
1257 exp.value.str = NULL;
1258 return exp;
1259}
1260
1261static struct chanData*
1262register_channel(struct chanNode *cNode, char *registrar)
1263{
1264 struct chanData *channel;
1265 enum levelOption lvlOpt;
1266 enum charOption chOpt;
1267
1268 channel = calloc(1, sizeof(struct chanData));
1269
1270 channel->notes = dict_new();
1271 dict_set_free_data(channel->notes, chanserv_free_note);
1272
1273 channel->registrar = strdup(registrar);
1274 channel->registered = now;
1275 channel->visited = now;
1276 channel->limitAdjusted = now;
a32da4c7 1277 channel->ownerTransfer = now;
d76ed9a9
AS
1278 channel->flags = CHANNEL_DEFAULT_FLAGS;
1279 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1280 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1281 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1282 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1283
1284 channel->prev = NULL;
1285 channel->next = channelList;
1286
1287 if(channelList)
1288 channelList->prev = channel;
1289 channelList = channel;
1290 registered_channels++;
1291
1292 channel->channel = cNode;
1293 LockChannel(cNode);
1294 cNode->channel_info = channel;
1295
1296 return channel;
1297}
1298
1299static struct userData*
dfaa28a4 1300add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info, time_t accessexpiry)
d76ed9a9
AS
1301{
1302 struct userData *ud;
1303
1304 if(access > UL_OWNER)
1305 return NULL;
1306
1307 ud = calloc(1, sizeof(*ud));
1308 ud->channel = channel;
1309 ud->handle = handle;
1310 ud->seen = seen;
1311 ud->access = access;
1312 ud->info = info ? strdup(info) : NULL;
dfaa28a4 1313 ud->accessexpiry = accessexpiry ? accessexpiry : 0;
1314 ud->clvlexpiry = 0;
1315 ud->lastaccess = 0;
d76ed9a9
AS
1316
1317 ud->prev = NULL;
1318 ud->next = channel->users;
1319 if(channel->users)
1320 channel->users->prev = ud;
1321 channel->users = ud;
1322
1323 channel->userCount++;
1324 userCount++;
1325
1326 ud->u_prev = NULL;
1327 ud->u_next = ud->handle->channels;
1328 if(ud->u_next)
1329 ud->u_next->u_prev = ud;
1330 ud->handle->channels = ud;
1331
c8ca69a0 1332 ud->flags = USER_FLAGS_DEFAULT;
d76ed9a9
AS
1333 return ud;
1334}
1335
1336static void unregister_channel(struct chanData *channel, const char *reason);
1337
fd20b142 1338static void
1339chanserv_expire_tempuser(void *data)
1340{
1341 struct userData *uData = data;
1342 char *handle;
1343
1344 if (data) {
1345 handle = strdup(uData->handle->handle);
dfaa28a4 1346 if (uData->accessexpiry > 0) {
fd20b142 1347 if (uData->present) {
1348 struct userNode *user, *next_un = NULL;
1349 struct handle_info *hi;
1350
1351 hi = get_handle_info(handle);
1352 for (user = hi->users; user; user = next_un) {
1353 struct mod_chanmode *change;
1354 struct modeNode *mn;
1355 unsigned int count = 0;
1356
1357 send_message(user, chanserv, "CSMSG_AUTO_DELETED", chanserv->nick, uData->channel->channel->name);
1358 if (!(mn = GetUserMode(uData->channel->channel, user)) || !(mn->modes & MODE_CHANOP)) {
1359 next_un = user->next_authed;
1360 continue;
1361 }
1362
1363 change = mod_chanmode_alloc(2);
1364 change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
1365 change->args[count++].u.member = mn;
1366
1367 if (count) {
1368 change->argc = count;
1369 mod_chanmode_announce(chanserv, uData->channel->channel, change);
1370 }
1371 mod_chanmode_free(change);
1372 next_un = user->next_authed;
1373 }
1374 }
1375 del_channel_user(uData, 1);
1376 }
1377 }
1378}
1379
dfaa28a4 1380static void
1381chanserv_expire_tempclvl(void *data)
1382{
1383 struct userData *uData = data;
1384 char *handle;
1385
1386 if (data) {
1387 handle = strdup(uData->handle->handle);
1388 if (uData->clvlexpiry > 0) {
1389 int changemodes = 0;
1390 unsigned int mode = 0;
1391
1392 if (((uData->lastaccess == UL_PEON) || (uData->lastaccess == UL_HALFOP)) && (uData->access >= UL_OP)) {
1393 changemodes = 1;
1394 mode = MODE_REMOVE | MODE_CHANOP;
1395 } else if ((uData->lastaccess == UL_PEON) && (uData->access == UL_HALFOP)) {
1396 changemodes = 1;
1397 mode = MODE_REMOVE | MODE_HALFOP;
1398 } else
1399 changemodes = 0;
1400
1401 if (uData->present) {
1402 struct userNode *user, *next_un = NULL;
1403 struct handle_info *hi;
1404
1405 hi = get_handle_info(handle);
1406 for (user = hi->users; user; user = next_un) {
1407 struct mod_chanmode *change;
1408 struct modeNode *mn;
1409 unsigned int count = 0;
1410
1411 send_message(user, chanserv, "CSMSG_CLVL_EXPIRED", uData->channel->channel->name);
1412 if (!(mn = GetUserMode(uData->channel->channel, user))) {
1413 next_un = user->next_authed;
1414 continue;
1415 }
1416
1417 if (changemodes == 0) {
1418 next_un = user->next_authed;
1419 continue;
1420 }
1421
1422 change = mod_chanmode_alloc(2);
1423 change->args[count].mode = mode;
1424 change->args[count++].u.member = mn;
1425
1426 if (count) {
1427 change->argc = count;
1428 mod_chanmode_announce(chanserv, uData->channel->channel, change);
1429 }
1430 mod_chanmode_free(change);
1431 next_un = user->next_authed;
1432 }
1433 }
1434
1435 uData->access = uData->lastaccess;
1436 uData->lastaccess = 0;
1437 uData->clvlexpiry = 0;
1438 }
1439 }
1440}
1441
d76ed9a9
AS
1442void
1443del_channel_user(struct userData *user, int do_gc)
1444{
1445 struct chanData *channel = user->channel;
1446
1447 channel->userCount--;
1448 userCount--;
1449
fd20b142 1450 timeq_del(0, chanserv_expire_tempuser, user, TIMEQ_IGNORE_WHEN);
dfaa28a4 1451 timeq_del(0, chanserv_expire_tempclvl, user, TIMEQ_IGNORE_WHEN);
fd20b142 1452
d76ed9a9
AS
1453 if(user->prev)
1454 user->prev->next = user->next;
1455 else
1456 channel->users = user->next;
1457 if(user->next)
1458 user->next->prev = user->prev;
1459
1460 if(user->u_prev)
1461 user->u_prev->u_next = user->u_next;
1462 else
1463 user->handle->channels = user->u_next;
1464 if(user->u_next)
1465 user->u_next->u_prev = user->u_prev;
1466
1467 free(user->info);
1468 free(user);
63c95a47 1469 if(do_gc && !channel->users && !IsProtected(channel)) {
1470 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
d76ed9a9 1471 unregister_channel(channel, "lost all users.");
63c95a47 1472 }
d76ed9a9
AS
1473}
1474
ac3bdc8d
AS
1475static struct adduserPending*
1476add_adduser_pending(struct chanNode *channel, struct userNode *user, int level)
1477{
1478 struct adduserPending *ap;
1479 ap = calloc(1,sizeof(struct adduserPending));
1480 ap->channel = channel;
1481 ap->user = user;
1482 ap->level = level;
1483 ap->created = time(NULL);
1484
1485 /* ap->prev defaults to NULL already.. */
1486 ap->next = adduser_pendings;
1487 if(adduser_pendings)
1488 adduser_pendings->prev = ap;
1489 adduser_pendings = ap;
1490 adduser_pendings_count++;
1491 return(ap);
1492}
1493
1494static void
1495del_adduser_pending(struct adduserPending *ap)
1496{
1497 if(ap->prev)
1498 ap->prev->next = ap->next;
1499 else
1500 adduser_pendings = ap->next;
1501
1502 if(ap->next)
1503 ap->next->prev = ap->prev;
1504 free(ap);
1505}
1506
1507static void expire_adduser_pending();
1508
1509/* find_adduser_pending(channel, user) will find an arbitrary record
1510 * from user, channel, or user and channel.
1511 * if user or channel are NULL, they will match any records.
1512 */
1513static struct adduserPending*
1514find_adduser_pending(struct chanNode *channel, struct userNode *user)
1515{
1516 struct adduserPending *ap;
1517
1518 expire_adduser_pending(); /* why not here.. */
1519
1520 if(!channel && !user) /* 2 nulls matches all */
1521 return(adduser_pendings);
1522 for(ap = adduser_pendings;ap;ap = ap->next)
1523 {
1524 if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL))
1525 return ap;
1526 }
1527 return NULL;
1528}
1529
1530
1531/* Remove all pendings for a user or channel
1532 *
1533 * called in nickserv.c DelUser() and proto-* unregister_channel()
1534 */
1535void
1536wipe_adduser_pending(struct chanNode *channel, struct userNode *user)
1537{
1538 struct adduserPending *ap;
1539
1540 /* So this is a bit wastefull, i hate dealing with linked lists.
1541 * if its a problem we'll rewrite it right */
1542 while((ap = find_adduser_pending(channel, user))) {
1543 del_adduser_pending(ap);
1544 }
1545}
1546
1547/* Called from nickserv.c cmd_auth after someone auths */
1548void
1549process_adduser_pending(struct userNode *user)
1550{
1551 struct adduserPending *ap;
a03d6c77
AS
1552 if(!user->handle_info)
1553 return; /* not associated with an account */
ac3bdc8d
AS
1554 while((ap = find_adduser_pending(NULL, user)))
1555 {
1556 struct userData *actee;
5ca901c3
AS
1557 if(GetTrueChannelAccess(ap->channel->channel_info, ap->user->handle_info))
1558 {
1559 /* Already on the userlist. do nothing*/
1560 }
1561 else
1562 {
fd20b142 1563 actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL, 0);
5ca901c3
AS
1564 scan_user_presence(actee, NULL);
1565 }
ac3bdc8d
AS
1566 del_adduser_pending(ap);
1567 }
1568}
1569
1570static void
1571expire_adduser_pending()
1572{
1573 struct adduserPending *ap, *ap_next;
1574 ap = adduser_pendings;
1575 while(ap)
1576 {
1577 if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL))
1578 { /* expire it */
1579 ap_next = ap->next; /* save next */
1580 del_adduser_pending(ap); /* free and relink */
1581 ap = ap_next; /* advance */
1582 }
1583 else
1584 ap = ap->next;
1585 }
1586}
1587
d76ed9a9
AS
1588static void expire_ban(void *data);
1589
63c95a47 1590struct banData*
d76ed9a9
AS
1591add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1592{
1593 struct banData *bd;
1594 unsigned int ii, l1, l2;
1595
1596 if(!mask)
1597 return NULL;
1598
1599 bd = malloc(sizeof(struct banData));
1600
1601 bd->channel = channel;
1602 bd->set = set;
1603 bd->triggered = triggered;
1604 bd->expires = expires;
1605
1606 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1607 {
1608 extern const char *hidden_host_suffix;
1609 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1610 char *new_mask;
1611
1612 l1 = strlen(mask);
1613 l2 = strlen(old_name);
1614 if(l2+2 > l1)
1615 continue;
1616 if(irccasecmp(mask + l1 - l2, old_name))
1617 continue;
1618 new_mask = alloca(MAXLEN);
a32da4c7 1619 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
d76ed9a9
AS
1620 mask = new_mask;
1621 }
1622 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1623 if(owner)
1624 safestrncpy(bd->owner, owner, sizeof(bd->owner));
ec1a68c8 1625 bd->reason = strdup(reason);
d76ed9a9
AS
1626
1627 if(expires)
1628 timeq_add(expires, expire_ban, bd);
1629
1630 bd->prev = NULL;
c8273589 1631 bd->next = channel->bans; /* lamers */
d76ed9a9
AS
1632 if(channel->bans)
1633 channel->bans->prev = bd;
1634 channel->bans = bd;
1635 channel->banCount++;
1636 banCount++;
1637
1638 return bd;
1639}
1640
1641static void
1642del_channel_ban(struct banData *ban)
1643{
1644 ban->channel->banCount--;
1645 banCount--;
1646
1647 if(ban->prev)
1648 ban->prev->next = ban->next;
1649 else
1650 ban->channel->bans = ban->next;
1651
1652 if(ban->next)
1653 ban->next->prev = ban->prev;
1654
1655 if(ban->expires)
1656 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1657
1658 if(ban->reason)
1659 free(ban->reason);
1660
1661 free(ban);
1662}
1663
1664static void
c8273589 1665expire_ban(void *data) /* lamer.. */
d76ed9a9
AS
1666{
1667 struct banData *bd = data;
1668 if(!IsSuspended(bd->channel))
1669 {
1670 struct banList bans;
1671 struct mod_chanmode change;
1672 unsigned int ii;
1673 bans = bd->channel->channel->banlist;
1674 mod_chanmode_init(&change);
1675 for(ii=0; ii<bans.used; ii++)
1676 {
1677 if(!strcmp(bans.list[ii]->ban, bd->mask))
1678 {
1679 change.argc = 1;
1680 change.args[0].mode = MODE_REMOVE|MODE_BAN;
a32da4c7 1681 change.args[0].u.hostmask = bd->mask;
d76ed9a9
AS
1682 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1683 break;
1684 }
1685 }
1686 }
1687 bd->expires = 0;
1688 del_channel_ban(bd);
1689}
1690
1691static void chanserv_expire_suspension(void *data);
1692
1693static void
1694unregister_channel(struct chanData *channel, const char *reason)
1695{
1696 struct mod_chanmode change;
1697 char msgbuf[MAXLEN];
1698
1699 /* After channel unregistration, the following must be cleaned
1700 up:
1701 - Channel information.
1702 - Channel users.
c8273589 1703 - Channel bans. (lamers)
d76ed9a9 1704 - Channel suspension data.
ac3bdc8d 1705 - adduser_pending data.
d76ed9a9
AS
1706 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1707 */
1708
1709 if(!channel)
1710 return;
1711
1712 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1713
1117fc5a 1714 if(off_channel > 0)
a32da4c7 1715 {
1716 mod_chanmode_init(&change);
1717 change.modes_clear |= MODE_REGISTERED;
1718 mod_chanmode_announce(chanserv, channel->channel, &change);
1719 }
d76ed9a9 1720
ac3bdc8d
AS
1721 wipe_adduser_pending(channel->channel, NULL);
1722
d76ed9a9
AS
1723 while(channel->users)
1724 del_channel_user(channel->users, 0);
1725
1726 while(channel->bans)
1727 del_channel_ban(channel->bans);
1728
1729 free(channel->topic);
1730 free(channel->registrar);
1731 free(channel->greeting);
1732 free(channel->user_greeting);
1733 free(channel->topic_mask);
1734
1735 if(channel->prev)
1736 channel->prev->next = channel->next;
1737 else
1738 channelList = channel->next;
1739
1740 if(channel->next)
1741 channel->next->prev = channel->prev;
1742
1743 if(channel->suspended)
1744 {
1745 struct chanNode *cNode = channel->channel;
1746 struct suspended *suspended, *next_suspended;
1747
1748 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1749 {
1750 next_suspended = suspended->previous;
1751 free(suspended->suspender);
1752 free(suspended->reason);
1753 if(suspended->expires)
1754 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1755 free(suspended);
1756 }
1757
1758 if(cNode)
1759 cNode->channel_info = NULL;
1760 }
1761 channel->channel->channel_info = NULL;
1762
1763 dict_delete(channel->notes);
1764 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1765 if(!IsSuspended(channel))
1766 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1767 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1768 UnlockChannel(channel->channel);
1769 free(channel);
1770 registered_channels--;
1771}
1772
1773static void
1774expire_channels(UNUSED_ARG(void *data))
1775{
1776 struct chanData *channel, *next;
1777 struct userData *user;
1778 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1779
1780 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1781 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1782
1783 for(channel = channelList; channel; channel = next)
1784 {
1785 next = channel->next;
1786
1787 /* See if the channel can be expired. */
1788 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1789 || IsProtected(channel))
1790 continue;
1791
1792 /* Make sure there are no high-ranking users still in the channel. */
1793 for(user=channel->users; user; user=user->next)
1794 if(user->present && (user->access >= UL_PRESENT))
1795 break;
1796 if(user)
1797 continue;
1798
1799 /* Unregister the channel */
1800 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
63c95a47 1801 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
d76ed9a9
AS
1802 unregister_channel(channel, "registration expired.");
1803 }
1804
1805 if(chanserv_conf.channel_expire_frequency)
1806 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1807}
1808
1809static int
ff5f1ab2 1810protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel, int protect_invitables)
d76ed9a9
AS
1811{
1812 char protect = channel->chOpts[chProtect];
1813 struct userData *cs_victim, *cs_aggressor;
1814
ff5f1ab2
AS
1815 /* If victim access level is greater than set invitelevel, don't let
1816 * us kick them, but don't consider it punishment if someone else does
1817 */
d76ed9a9 1818
ff5f1ab2
AS
1819
1820 if(victim == aggressor)
1821 return 0;
d76ed9a9
AS
1822 /* Don't protect if the victim isn't authenticated (because they
1823 can't be a channel user), unless we are to protect non-users
1824 also. */
ff5f1ab2 1825
d76ed9a9 1826 cs_victim = GetChannelAccess(channel, victim->handle_info);
ff5f1ab2
AS
1827
1828 /* If they have enough access to invite themselvs through a ban,
1829 * and its us kicking them, don't. -Rubin */
1830 if(protect_invitables==true && cs_victim && (cs_victim->access >= channel->lvlOpts[lvlInviteMe]))
1831 return 1;
1832
1833 if(protect == 'n')
1834 return 0;
1835
d76ed9a9
AS
1836 if(protect != 'a' && !cs_victim)
1837 return 0;
1838
1839 /* Protect if the aggressor isn't a user because at this point,
1840 the aggressor can only be less than or equal to the victim. */
ff5f1ab2
AS
1841
1842 /* Not protected from chanserv except above */
1843 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1844 if(aggressor == chanserv)
1845 return 0;
1846
d76ed9a9
AS
1847 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1848 if(!cs_aggressor)
1849 return 1;
1850
1851 /* If the aggressor was a user, then the victim can't be helped. */
1852 if(!cs_victim)
1853 return 0;
1854
1855 switch(protect)
1856 {
1857 case 'l':
1858 if(cs_victim->access > cs_aggressor->access)
1859 return 1;
1860 break;
1861 case 'a':
1862 case 'e':
1863 if(cs_victim->access >= cs_aggressor->access)
1864 return 1;
1865 break;
1866 }
1867
1868 return 0;
1869}
1870
1871static int
c092fcad 1872validate_op(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
d76ed9a9
AS
1873{
1874 struct chanData *cData = channel->channel_info;
1875 struct userData *cs_victim;
1876
1877 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
b75e24a3 1878 || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9
AS
1879 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1880 {
c092fcad
AS
1881 if(cmd)
1882 reply("CSMSG_OPBY_LOCKED");
1883 else
1884 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
d76ed9a9
AS
1885 return 0;
1886 }
1887
1888 return 1;
1889}
1890
55342ce8 1891static int
c092fcad 1892validate_halfop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
55342ce8 1893{
1894 struct chanData *cData = channel->channel_info;
1895 struct userData *cs_victim;
1896
1897 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
b75e24a3 1898 || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */))
55342ce8 1899 && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0))
1900 {
c092fcad 1901 reply("CSMSG_HOPBY_LOCKED");
55342ce8 1902 return 0;
1903 }
1904
1905 return 1;
1906}
1907
1908
d76ed9a9 1909static int
c092fcad 1910validate_deop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
d76ed9a9
AS
1911{
1912 if(IsService(victim))
1913 {
c092fcad 1914 reply("MSG_SERVICE_IMMUNE", victim->nick);
d76ed9a9
AS
1915 return 0;
1916 }
1917
ff5f1ab2 1918 if(protect_user(victim, user, channel->channel_info, false))
d76ed9a9 1919 {
c092fcad 1920 reply("CSMSG_USER_PROTECTED", victim->nick);
d76ed9a9
AS
1921 return 0;
1922 }
1923
1924 return 1;
1925}
1926
55342ce8 1927static int
c092fcad 1928validate_dehop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
55342ce8 1929{
1930 if(IsService(victim))
1931 {
c092fcad 1932 reply("MSG_SERVICE_IMMUNE", victim->nick);
55342ce8 1933 return 0;
1934 }
1935
ff5f1ab2 1936 if(protect_user(victim, user, channel->channel_info, false))
55342ce8 1937 {
c092fcad 1938 reply("CSMSG_USER_PROTECTED", victim->nick);
55342ce8 1939 return 0;
1940 }
1941
1942 return 1;
1943}
1944
d76ed9a9 1945static struct do_not_register *
0f6fe38c 1946chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
d76ed9a9
AS
1947{
1948 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1949 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1950 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1951 strcpy(dnr->reason, reason);
1952 dnr->set = now;
1953 if(dnr->chan_name[0] == '*')
1954 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1955 else if(strpbrk(dnr->chan_name, "*?"))
1956 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1957 else
1958 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1959 return dnr;
1960}
1961
1962static struct dnrList
0f6fe38c 1963chanserv_find_dnrs(const char *chan_name, const char *handle)
d76ed9a9
AS
1964{
1965 struct dnrList list;
0f6fe38c 1966 dict_iterator_t it;
d76ed9a9
AS
1967 struct do_not_register *dnr;
1968
1969 dnrList_init(&list);
1117fc5a 1970 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
d76ed9a9
AS
1971 dnrList_append(&list, dnr);
1972 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1973 dnrList_append(&list, dnr);
1974 if(chan_name)
0f6fe38c 1975 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1976 if(match_ircglob(chan_name, iter_key(it)))
1977 dnrList_append(&list, iter_data(it));
d76ed9a9
AS
1978 return list;
1979}
1980
1981static unsigned int
1117fc5a 1982chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
d76ed9a9
AS
1983{
1984 struct dnrList list;
0f6fe38c 1985 struct do_not_register *dnr;
d76ed9a9 1986 unsigned int ii;
0f6fe38c 1987 char buf[INTERVALLEN];
d76ed9a9 1988
0f6fe38c 1989 list = chanserv_find_dnrs(chan_name, handle);
d76ed9a9 1990 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
0f6fe38c 1991 {
1992 dnr = list.list[ii];
1993 if(dnr->set)
1994 {
1995 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1996 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1997 }
1998 else
1999 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
2000 }
d76ed9a9
AS
2001 if(ii < list.used)
2002 reply("CSMSG_MORE_DNRS", list.used - ii);
2003 free(list.list);
2004 return ii;
2005}
2006
2007struct do_not_register *
2008chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
2009{
2010 struct do_not_register *dnr;
0f6fe38c 2011 dict_iterator_t it;
d76ed9a9 2012
0f6fe38c 2013 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
2014 return dnr;
2015 if(chan_name)
d76ed9a9 2016 {
0f6fe38c 2017 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
2018 return dnr;
2019 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
2020 if(match_ircglob(chan_name, iter_key(it)))
2021 return iter_data(it);
d76ed9a9 2022 }
0f6fe38c 2023 return NULL;
d76ed9a9
AS
2024}
2025
2026static CHANSERV_FUNC(cmd_noregister)
2027{
2028 const char *target;
0f6fe38c 2029 struct do_not_register *dnr;
2030 char buf[INTERVALLEN];
d76ed9a9
AS
2031 unsigned int matches;
2032
2033 if(argc < 2)
2034 {
0f6fe38c 2035 dict_iterator_t it;
2036
d76ed9a9 2037 reply("CSMSG_DNR_SEARCH_RESULTS");
0f6fe38c 2038 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
2039 reply("CSMSG_BAR");
2040 matches = 0;
2041 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
2042 {
2043 dnr = iter_data(it);
2044 if(dnr->set)
2045 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
2046 else
2047 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
2048 matches++;
2049 }
2050 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
2051 {
2052 dnr = iter_data(it);
2053 if(dnr->set)
2054 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
2055 else
2056 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
2057 matches++;
2058 }
2059 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
2060 {
2061 dnr = iter_data(it);
2062 if(dnr->set)
2063 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
2064 else
2065 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
2066 matches++;
2067 }
2068
d76ed9a9
AS
2069 if(matches)
2070 reply("MSG_MATCH_COUNT", matches);
2071 else
2072 reply("MSG_NO_MATCHES");
2073 return 0;
2074 }
2075
2076 target = argv[1];
2077
2078 if(!IsChannelName(target) && (*target != '*'))
2079 {
2080 reply("CSMSG_NOT_DNR", target);
2081 return 0;
2082 }
2083
2084 if(argc > 2)
2085 {
0f6fe38c 2086 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
d76ed9a9
AS
2087 if((*target == '*') && !get_handle_info(target + 1))
2088 {
2089 reply("MSG_HANDLE_UNKNOWN", target + 1);
2090 return 0;
2091 }
0f6fe38c 2092 chanserv_add_dnr(target, user->handle_info->handle, reason);
d76ed9a9
AS
2093 reply("CSMSG_NOREGISTER_CHANNEL", target);
2094 return 1;
2095 }
2096
2097 reply("CSMSG_DNR_SEARCH_RESULTS");
0f6fe38c 2098 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
2099 reply("CSMSG_BAR");
d76ed9a9 2100 if(*target == '*')
1117fc5a 2101 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
d76ed9a9
AS
2102 else
2103 matches = chanserv_show_dnrs(user, cmd, target, NULL);
2104 if(!matches)
2105 reply("MSG_NO_MATCHES");
2106 return 0;
2107}
2108
2109static CHANSERV_FUNC(cmd_allowregister)
2110{
2111 const char *chan_name = argv[1];
2112
0f6fe38c 2113 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
d76ed9a9 2114 {
0f6fe38c 2115 dict_remove(handle_dnrs, chan_name+1);
d76ed9a9 2116 reply("CSMSG_DNR_REMOVED", chan_name);
ff3b058a 2117 }
0f6fe38c 2118 else if(dict_find(plain_dnrs, chan_name, NULL))
d76ed9a9 2119 {
0f6fe38c 2120 dict_remove(plain_dnrs, chan_name);
2121 reply("CSMSG_DNR_REMOVED", chan_name);
ff3b058a 2122 }
0f6fe38c 2123 else if(dict_find(mask_dnrs, chan_name, NULL))
ff3b058a 2124 {
0f6fe38c 2125 dict_remove(mask_dnrs, chan_name);
2126 reply("CSMSG_DNR_REMOVED", chan_name);
d76ed9a9 2127 }
ff3b058a 2128 else
2129 {
0f6fe38c 2130 reply("CSMSG_NO_SUCH_DNR", chan_name);
ff3b058a 2131 return 0;
2132 }
d76ed9a9
AS
2133 return 1;
2134}
2135
2136unsigned int
2137chanserv_get_owned_count(struct handle_info *hi)
2138{
2139 struct userData *cList;
2140 unsigned int owned;
2141
2142 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2143 if(cList->access == UL_OWNER)
2144 owned++;
2145 return owned;
2146}
2147
2148static CHANSERV_FUNC(cmd_register)
2149{
d76ed9a9
AS
2150 struct handle_info *handle;
2151 struct chanData *cData;
2152 struct modeNode *mn;
2153 char reason[MAXLEN];
2154 char *chan_name;
2155 unsigned int new_channel, force=0;
2156 struct do_not_register *dnr;
f8f30e75
AS
2157 unsigned int n;
2158
08895577 2159 if (checkDefCon(DEFCON_NO_NEW_CHANNELS) && !IsOper(user)) {
2160 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2161 return 0;
2162 }
d76ed9a9
AS
2163
2164 if(channel)
2165 {
2166 if(channel->channel_info)
2167 {
2168 reply("CSMSG_ALREADY_REGGED", channel->name);
2169 return 0;
2170 }
2171
2172 if(channel->bad_channel)
2173 {
2174 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2175 return 0;
2176 }
2177
f8f30e75 2178 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
d76ed9a9
AS
2179 {
2180 reply("CSMSG_MUST_BE_OPPED", channel->name);
2181 return 0;
2182 }
2183
2184 new_channel = 0;
2185 chan_name = channel->name;
2186 }
2187 else
2188 {
fc8798ec
AS
2189 if(argc < 2)
2190 {
2191 reply("MSG_MISSING_PARAMS", cmd->name);
567a5f26 2192 svccmd_send_help_brief(user, chanserv, cmd);
fc8798ec
AS
2193 return 0;
2194 }
2195 if(!IsChannelName(argv[1]))
d76ed9a9
AS
2196 {
2197 reply("MSG_NOT_CHANNEL_NAME");
2198 return 0;
2199 }
2200
2201 if(opserv_bad_channel(argv[1]))
2202 {
2203 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2204 return 0;
2205 }
2206
2207 new_channel = 1;
2208 chan_name = argv[1];
2209 }
2210
2211 if(argc >= (new_channel+2))
2212 {
2213 if(!IsHelping(user))
2214 {
2215 reply("CSMSG_PROXY_FORBIDDEN");
2216 return 0;
2217 }
2218
2219 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2220 return 0;
2221 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2222 dnr = chanserv_is_dnr(chan_name, handle);
f8f30e75
AS
2223
2224 /* Check if they are over the limit.. */
2225 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2226 {
2227 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2228 return 0;
2229 }
2230
d76ed9a9
AS
2231 }
2232 else
2233 {
f8f30e75 2234 handle = user->handle_info;
d76ed9a9 2235 dnr = chanserv_is_dnr(chan_name, handle);
f8f30e75
AS
2236 /* Check if they are over the limit.. */
2237 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2238 {
2239 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned);
2240 return 0;
2241 }
2242 /* Check if another service is in the channel */
2243 if(channel)
2244 for(n = 0; n < channel->members.used; n++)
2245 {
2246 mn = channel->members.list[n];
2247 if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user))
2248 {
2249 reply("CSMSG_ANOTHER_SERVICE");
2250 return 0;
2251 }
2252 }
d76ed9a9
AS
2253 }
2254 if(dnr && !force)
2255 {
2256 if(!IsHelping(user))
2257 reply("CSMSG_DNR_CHANNEL", chan_name);
2258 else
1117fc5a 2259 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
d76ed9a9
AS
2260 return 0;
2261 }
2262
f8f30e75 2263 /* now handled above for message specilization *
d76ed9a9
AS
2264 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2265 {
2266 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2267 return 0;
2268 }
f8f30e75 2269 */
d76ed9a9
AS
2270
2271 if(new_channel)
2aef5f4b 2272 channel = AddChannel(argv[1], now, NULL, NULL, NULL);
d76ed9a9
AS
2273
2274 cData = register_channel(channel, user->handle_info->handle);
fd20b142 2275 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL, 0), NULL);
d76ed9a9 2276 cData->modes = chanserv_conf.default_modes;
1117fc5a 2277 if(off_channel > 0)
a32da4c7 2278 cData->modes.modes_set |= MODE_REGISTERED;
ec1a68c8 2279 if (IsOffChannel(cData))
2280 {
2281 mod_chanmode_announce(chanserv, channel, &cData->modes);
2282 }
2283 else
2284 {
2285 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2286 change->args[change->argc].mode = MODE_CHANOP;
2287 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2288 change->argc++;
2289 mod_chanmode_announce(chanserv, channel, change);
2290 mod_chanmode_free(change);
2291 }
d76ed9a9
AS
2292
2293 /* Initialize the channel's max user record. */
2294 cData->max = channel->members.used;
8b9e7d45 2295 cData->maxsetinfo = chanserv_conf.max_userinfo_length;
d76ed9a9
AS
2296
2297 if(handle != user->handle_info)
2298 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2299 else
d76ed9a9
AS
2300
2301 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
09a3057c 2302 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_REGISTERED_TO", channel->name,
2303 handle->handle, user->handle_info->handle);
d76ed9a9
AS
2304 return 1;
2305}
2306
2307static const char *
2308make_confirmation_string(struct userData *uData)
2309{
2310 static char strbuf[16];
2311 char *src;
2312 unsigned int accum;
2313
2314 accum = 0;
2315 for(src = uData->handle->handle; *src; )
2316 accum = accum * 31 + toupper(*src++);
2317 if(uData->channel)
2318 for(src = uData->channel->channel->name; *src; )
2319 accum = accum * 31 + toupper(*src++);
2320 sprintf(strbuf, "%08x", accum);
2321 return strbuf;
2322}
2323
2324static CHANSERV_FUNC(cmd_unregister)
2325{
2326 char *name;
2327 char reason[MAXLEN];
2328 struct chanData *cData;
2329 struct userData *uData;
2330
2331 cData = channel->channel_info;
2332 if(!cData)
2333 {
2334 reply("CSMSG_NOT_REGISTERED", channel->name);
2335 return 0;
2336 }
2337
2338 uData = GetChannelUser(cData, user->handle_info);
2339 if(!uData || (uData->access < UL_OWNER))
2340 {
2341 reply("CSMSG_NO_ACCESS");
2342 return 0;
2343 }
2344
2345 if(IsProtected(cData))
2346 {
2347 reply("CSMSG_UNREG_NODELETE", channel->name);
2348 return 0;
2349 }
2350
2351 if(!IsHelping(user))
2352 {
2353 const char *confirm_string;
2354 if(IsSuspended(cData))
2355 {
2356 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2357 return 0;
2358 }
2359 confirm_string = make_confirmation_string(uData);
2360 if((argc < 2) || strcmp(argv[1], confirm_string))
2361 {
fa0fac3e 2362 reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
d76ed9a9
AS
2363 return 0;
2364 }
2365 }
2366
2367 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2368 name = strdup(channel->name);
2369 unregister_channel(cData, reason);
63c95a47 2370 spamserv_cs_unregister(user, channel, manually, "unregistered");
d76ed9a9
AS
2371 reply("CSMSG_UNREG_SUCCESS", name);
2372 free(name);
2373 return 1;
2374}
2375
63c95a47 2376static void
2377ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2378{
2379 extern struct userNode *spamserv;
2380 struct mod_chanmode *change;
2381
2382 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2383 {
2384 change = mod_chanmode_alloc(2);
2385 change->argc = 2;
2386 change->args[0].mode = MODE_CHANOP;
2387 change->args[0].u.member = AddChannelUser(chanserv, channel);
2388 change->args[1].mode = MODE_CHANOP;
2389 change->args[1].u.member = AddChannelUser(spamserv, channel);
2390 }
2391 else
2392 {
2393 change = mod_chanmode_alloc(1);
2394 change->argc = 1;
2395 change->args[0].mode = MODE_CHANOP;
2396 change->args[0].u.member = AddChannelUser(chanserv, channel);
2397 }
2398
2399 mod_chanmode_announce(chanserv, channel, change);
2400 mod_chanmode_free(change);
2401}
2402
d76ed9a9
AS
2403static CHANSERV_FUNC(cmd_move)
2404{
1117fc5a 2405 struct mod_chanmode change;
d76ed9a9
AS
2406 struct chanNode *target;
2407 struct modeNode *mn;
2408 struct userData *uData;
d76ed9a9 2409 struct do_not_register *dnr;
63c95a47 2410 int chanserv_join = 0, spamserv_join;
d76ed9a9
AS
2411
2412 REQUIRE_PARAMS(2);
2413
2414 if(IsProtected(channel->channel_info))
2415 {
2416 reply("CSMSG_MOVE_NODELETE", channel->name);
2417 return 0;
2418 }
2419
2420 if(!IsChannelName(argv[1]))
2421 {
2422 reply("MSG_NOT_CHANNEL_NAME");
2423 return 0;
2424 }
2425
2426 if(opserv_bad_channel(argv[1]))
2427 {
2428 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2429 return 0;
2430 }
2431
2432 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2433 {
2434 for(uData = channel->channel_info->users; uData; uData = uData->next)
2435 {
2436 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2437 {
2438 if(!IsHelping(user))
2439 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2440 else
1117fc5a 2441 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
d76ed9a9
AS
2442 return 0;
2443 }
2444 }
2445 }
2446
1117fc5a 2447 mod_chanmode_init(&change);
d76ed9a9
AS
2448 if(!(target = GetChannel(argv[1])))
2449 {
2aef5f4b 2450 target = AddChannel(argv[1], now, NULL, NULL, NULL);
d76ed9a9 2451 if(!IsSuspended(channel->channel_info))
63c95a47 2452 chanserv_join = 1;
d76ed9a9
AS
2453 }
2454 else if(target->channel_info)
2455 {
2456 reply("CSMSG_ALREADY_REGGED", target->name);
2457 return 0;
2458 }
2459 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2460 && !IsHelping(user))
2461 {
2462 reply("CSMSG_MUST_BE_OPPED", target->name);
2463 return 0;
2464 }
2465 else if(!IsSuspended(channel->channel_info))
63c95a47 2466 chanserv_join = 1;
d76ed9a9 2467
1117fc5a 2468 if(off_channel > 0)
2469 {
2470 /* Clear MODE_REGISTERED from old channel, add it to new. */
2471 change.argc = 0;
2472 change.modes_clear = MODE_REGISTERED;
2473 mod_chanmode_announce(chanserv, channel, &change);
2474 change.modes_clear = 0;
2475 change.modes_set = MODE_REGISTERED;
2476 mod_chanmode_announce(chanserv, target, &change);
2477 }
2478
d76ed9a9
AS
2479 /* Move the channel_info to the target channel; it
2480 shouldn't be necessary to clear timeq callbacks
2481 for the old channel. */
2482 target->channel_info = channel->channel_info;
2483 target->channel_info->channel = target;
2484 channel->channel_info = NULL;
2485
63c95a47 2486 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2487
2488 if (chanserv_join)
2489 ss_cs_join_channel(target, spamserv_join);
d76ed9a9 2490
d76ed9a9
AS
2491 if(!IsSuspended(target->channel_info))
2492 {
2493 char reason2[MAXLEN];
09a3057c 2494 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2495 DelChannelUser(chanserv, channel, reason2, 0);
d76ed9a9 2496 }
09a3057c 2497
d76ed9a9
AS
2498 UnlockChannel(channel);
2499 LockChannel(target);
09a3057c 2500 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_CHANNEL_MOVED",
2501 channel->name, target->name, user->handle_info->handle);
2502
63c95a47 2503 reply("CSMSG_MOVE_SUCCESS", target->name);
d76ed9a9
AS
2504 return 1;
2505}
2506
2507static void
2508merge_users(struct chanData *source, struct chanData *target)
2509{
2510 struct userData *suData, *tuData, *next;
2511 dict_iterator_t it;
2512 dict_t merge;
2513
2514 merge = dict_new();
2515
2516 /* Insert the source's users into the scratch area. */
2517 for(suData = source->users; suData; suData = suData->next)
2518 dict_insert(merge, suData->handle->handle, suData);
2519
2520 /* Iterate through the target's users, looking for
2521 users common to both channels. The lower access is
2522 removed from either the scratch area or target user
2523 list. */
2524 for(tuData = target->users; tuData; tuData = next)
2525 {
2526 struct userData *choice;
2527
2528 next = tuData->next;
2529
2530 /* If a source user exists with the same handle as a target
2531 channel's user, resolve the conflict by removing one. */
2532 suData = dict_find(merge, tuData->handle->handle, NULL);
2533 if(!suData)
2534 continue;
2535
2536 /* Pick the data we want to keep. */
2537 /* If the access is the same, use the later seen time. */
2538 if(suData->access == tuData->access)
2539 choice = (suData->seen > tuData->seen) ? suData : tuData;
2540 else /* Otherwise, keep the higher access level. */
2541 choice = (suData->access > tuData->access) ? suData : tuData;
2542
2543 /* Remove the user that wasn't picked. */
2544 if(choice == tuData)
2545 {
2546 dict_remove(merge, suData->handle->handle);
2547 del_channel_user(suData, 0);
2548 }
2549 else
2550 del_channel_user(tuData, 0);
2551 }
2552
2553 /* Move the remaining users to the target channel. */
2554 for(it = dict_first(merge); it; it = iter_next(it))
2555 {
2556 suData = iter_data(it);
2557
2558 /* Insert the user into the target channel's linked list. */
2559 suData->prev = NULL;
2560 suData->next = target->users;
2561 suData->channel = target;
2562
2563 if(target->users)
2564 target->users->prev = suData;
2565 target->users = suData;
2566
2567 /* Update the user counts for the target channel; the
2568 source counts are left alone. */
2569 target->userCount++;
2570 }
2571
2572 /* Possible to assert (source->users == NULL) here. */
2573 source->users = NULL;
2574 dict_delete(merge);
2575}
2576
2577static void
2578merge_bans(struct chanData *source, struct chanData *target)
2579{
2580 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2581
2582 /* Hold on to the original head of the target ban list
2583 to avoid comparing source bans with source bans. */
2584 tFront = target->bans;
2585
2586 /* Perform a totally expensive O(n*m) merge, ick. */
2587 for(sbData = source->bans; sbData; sbData = sNext)
2588 {
2589 /* Flag to track whether the ban's been moved
2590 to the destination yet. */
2591 int moved = 0;
2592
2593 /* Possible to assert (sbData->prev == NULL) here. */
2594 sNext = sbData->next;
2595
2596 for(tbData = tFront; tbData; tbData = tNext)
2597 {
2598 tNext = tbData->next;
2599
2600 /* Perform two comparisons between each source
2601 and target ban, conflicts are resolved by
2602 keeping the broader ban and copying the later
2603 expiration and triggered time. */
2604 if(match_ircglobs(tbData->mask, sbData->mask))
2605 {
2606 /* There is a broader ban in the target channel that
2607 overrides one in the source channel; remove the
2608 source ban and break. */
2609 if(sbData->expires > tbData->expires)
2610 tbData->expires = sbData->expires;
2611 if(sbData->triggered > tbData->triggered)
2612 tbData->triggered = sbData->triggered;
2613 del_channel_ban(sbData);
2614 break;
2615 }
2616 else if(match_ircglobs(sbData->mask, tbData->mask))
2617 {
2618 /* There is a broader ban in the source channel that
2619 overrides one in the target channel; remove the
2620 target ban, fall through and move the source over. */
2621 if(tbData->expires > sbData->expires)
2622 sbData->expires = tbData->expires;
2623 if(tbData->triggered > sbData->triggered)
2624 sbData->triggered = tbData->triggered;
2625 if(tbData == tFront)
2626 tFront = tNext;
2627 del_channel_ban(tbData);
2628 }
2629
2630 /* Source bans can override multiple target bans, so
2631 we allow a source to run through this loop multiple
2632 times, but we can only move it once. */
2633 if(moved)
2634 continue;
2635 moved = 1;
2636
2637 /* Remove the source ban from the source ban list. */
2638 if(sbData->next)
2639 sbData->next->prev = sbData->prev;
2640
2641 /* Modify the source ban's associated channel. */
2642 sbData->channel = target;
2643
2644 /* Insert the ban into the target channel's linked list. */
2645 sbData->prev = NULL;
2646 sbData->next = target->bans;
2647
2648 if(target->bans)
2649 target->bans->prev = sbData;
2650 target->bans = sbData;
2651
2652 /* Update the user counts for the target channel. */
2653 target->banCount++;
2654 }
2655 }
2656
2657 /* Possible to assert (source->bans == NULL) here. */
2658 source->bans = NULL;
2659}
2660
2661static void
2662merge_data(struct chanData *source, struct chanData *target)
2663{
697f4c9a 2664 /* Use more recent visited and owner-transfer time; use older
2665 * registered time. Bitwise or may_opchan. Use higher max.
2666 * Do not touch last_refresh, ban count or user counts.
2667 */
d76ed9a9
AS
2668 if(source->visited > target->visited)
2669 target->visited = source->visited;
697f4c9a 2670 if(source->registered < target->registered)
2671 target->registered = source->registered;
2672 if(source->ownerTransfer > target->ownerTransfer)
2673 target->ownerTransfer = source->ownerTransfer;
2674 if(source->may_opchan)
2675 target->may_opchan = 1;
2676 if(source->max > target->max)
2677 target->max = source->max;
d76ed9a9
AS
2678}
2679
2680static void
2681merge_channel(struct chanData *source, struct chanData *target)
2682{
2683 merge_users(source, target);
2684 merge_bans(source, target);
2685 merge_data(source, target);
2686}
2687
2688static CHANSERV_FUNC(cmd_merge)
2689{
2690 struct userData *target_user;
2691 struct chanNode *target;
2692 char reason[MAXLEN];
2693
2694 REQUIRE_PARAMS(2);
2695
2696 /* Make sure the target channel exists and is registered to the user
2697 performing the command. */
2698 if(!(target = GetChannel(argv[1])))
2699 {
2700 reply("MSG_INVALID_CHANNEL");
2701 return 0;
2702 }
2703
2704 if(!target->channel_info)
2705 {
2706 reply("CSMSG_NOT_REGISTERED", target->name);
2707 return 0;
2708 }
2709
2710 if(IsProtected(channel->channel_info))
2711 {
2712 reply("CSMSG_MERGE_NODELETE");
2713 return 0;
2714 }
2715
2716 if(IsSuspended(target->channel_info))
2717 {
2718 reply("CSMSG_MERGE_SUSPENDED");
2719 return 0;
2720 }
2721
2722 if(channel == target)
2723 {
2724 reply("CSMSG_MERGE_SELF");
2725 return 0;
2726 }
2727
2728 target_user = GetChannelUser(target->channel_info, user->handle_info);
2729 if(!target_user || (target_user->access < UL_OWNER))
2730 {
2731 reply("CSMSG_MERGE_NOT_OWNER");
2732 return 0;
2733 }
2734
2735 /* Merge the channel structures and associated data. */
2736 merge_channel(channel->channel_info, target->channel_info);
63c95a47 2737 spamserv_cs_move_merge(user, channel, target, 0);
d76ed9a9
AS
2738 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2739 unregister_channel(channel->channel_info, reason);
2740 reply("CSMSG_MERGE_SUCCESS", target->name);
2741 return 1;
2742}
2743
2744static CHANSERV_FUNC(cmd_opchan)
2745{
2746 struct mod_chanmode change;
2747 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2748 {
2749 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2750 return 0;
2751 }
2752 channel->channel_info->may_opchan = 0;
2753 mod_chanmode_init(&change);
2754 change.argc = 1;
2755 change.args[0].mode = MODE_CHANOP;
a32da4c7 2756 change.args[0].u.member = GetUserMode(channel, chanserv);
d76ed9a9
AS
2757 mod_chanmode_announce(chanserv, channel, &change);
2758 reply("CSMSG_OPCHAN_DONE", channel->name);
2759 return 1;
2760}
2761
2762static CHANSERV_FUNC(cmd_adduser)
2763{
2764 struct userData *actee;
0f6fe38c 2765 struct userData *actor;
d76ed9a9 2766 struct handle_info *handle;
0f6fe38c 2767 unsigned short access;
d76ed9a9
AS
2768
2769 REQUIRE_PARAMS(3);
2770
2771 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2772 {
2773 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2774 return 0;
2775 }
2776
2777 access = user_level_from_name(argv[2], UL_OWNER);
2778 if(!access)
2779 {
2780 reply("CSMSG_INVALID_ACCESS", argv[2]);
2781 return 0;
2782 }
2783
2784 actor = GetChannelUser(channel->channel_info, user->handle_info);
2785 if(actor->access <= access)
2786 {
2787 reply("CSMSG_NO_BUMP_ACCESS");
2788 return 0;
2789 }
2790
2791 if(!(handle = modcmd_get_handle_info(user, argv[1])))
ac3bdc8d
AS
2792 {
2793 // 'kevin must first authenticate with AuthServ.' is sent to user
2794 struct userNode *unode;
2795 unode = GetUserH(argv[1]); /* find user struct by nick */
2796 if(unode)
2797 {
2798 if(find_adduser_pending(channel, unode)) {
2799 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name);
2800 }
2801 else {
2802 if(IsInChannel(channel, unode)) {
2803 reply("CSMSG_ADDUSER_PENDING");
2804 add_adduser_pending(channel, unode, access);
2805 send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
2806 }
2807 /* this results in user must auth AND not in chan errors. too confusing..
2808 else {
2809 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2810 }
2811 */
2812 }
2813 }
d76ed9a9 2814 return 0;
ac3bdc8d 2815 }
d76ed9a9
AS
2816
2817 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2818 {
a915f7d4 2819 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
d76ed9a9
AS
2820 return 0;
2821 }
2822
dfaa28a4 2823 time_t accessexpiry = 0;
fd20b142 2824 unsigned int duration = 0;
2825 if (argc > 3) {
2826 if ((duration = ParseInterval(argv[3])))
dfaa28a4 2827 accessexpiry = now + duration;
fd20b142 2828 }
2829
dfaa28a4 2830 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL, accessexpiry);
d76ed9a9 2831 scan_user_presence(actee, NULL);
fd20b142 2832
2833 if (duration > 0)
dfaa28a4 2834 timeq_add(accessexpiry, chanserv_expire_tempuser, actee);
fd20b142 2835
de9510bc 2836 reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
0f6fe38c 2837 return 1;
d76ed9a9
AS
2838}
2839
2840static CHANSERV_FUNC(cmd_clvl)
2841{
2842 struct handle_info *handle;
2843 struct userData *victim;
0f6fe38c 2844 struct userData *actor;
2845 unsigned short new_access;
d76ed9a9
AS
2846 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2847
2848 REQUIRE_PARAMS(3);
2849
2850 actor = GetChannelUser(channel->channel_info, user->handle_info);
2851
2852 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2853 return 0;
2854
2855 if(handle == user->handle_info && !privileged)
2856 {
2857 reply("CSMSG_NO_SELF_CLVL");
2858 return 0;
2859 }
2860
2861 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2862 {
2863 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2864 return 0;
2865 }
2866
2867 if(actor->access <= victim->access && !privileged)
2868 {
2869 reply("MSG_USER_OUTRANKED", handle->handle);
2870 return 0;
2871 }
2872
2873 new_access = user_level_from_name(argv[2], UL_OWNER);
2874
2875 if(!new_access)
2876 {
2877 reply("CSMSG_INVALID_ACCESS", argv[2]);
2878 return 0;
2879 }
2880
2881 if(new_access >= actor->access && !privileged)
2882 {
2883 reply("CSMSG_NO_BUMP_ACCESS");
2884 return 0;
2885 }
2886
dfaa28a4 2887 time_t clvlexpiry = 0;
2888 unsigned int duration = 0;
2889 if (argc > 3) {
2890 if ((duration = ParseInterval(argv[3])))
2891 clvlexpiry = now + duration;
2892 }
2893
2894 if (duration > 0) {
2895 if (victim->accessexpiry > 0) {
2896 reply("CSMSG_NO_BUMP_EXPIRY");
2897 return 0;
2898 }
2899
2900 victim->clvlexpiry = clvlexpiry;
2901 victim->lastaccess = victim->access;
2902 timeq_add(clvlexpiry, chanserv_expire_tempclvl, victim);
2903 }
2904
d76ed9a9 2905 victim->access = new_access;
de9510bc 2906 reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
0f6fe38c 2907 return 1;
d76ed9a9
AS
2908}
2909
2910static CHANSERV_FUNC(cmd_deluser)
2911{
2912 struct handle_info *handle;
2913 struct userData *victim;
0f6fe38c 2914 struct userData *actor;
2915 unsigned short access;
d76ed9a9
AS
2916 char *chan_name;
2917
2918 REQUIRE_PARAMS(2);
2919
2920 actor = GetChannelUser(channel->channel_info, user->handle_info);
2921
2922 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2923 return 0;
2924
2925 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2926 {
2927 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2928 return 0;
2929 }
2930
2931 if(argc > 2)
2932 {
2933 access = user_level_from_name(argv[1], UL_OWNER);
7b0150f8 2934 char *useraccess = user_level_name_from_level(victim->access);
d76ed9a9
AS
2935 if(!access)
2936 {
2937 reply("CSMSG_INVALID_ACCESS", argv[1]);
2938 return 0;
2939 }
7b0150f8 2940 if(strcasecmp(argv[1], useraccess))
d76ed9a9 2941 {
a915f7d4 2942 reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
d76ed9a9
AS
2943 return 0;
2944 }
2945 }
2946 else
2947 {
2948 access = victim->access;
2949 }
2950
2951 if((actor->access <= victim->access) && !IsHelping(user))
2952 {
2953 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2954 return 0;
2955 }
2956
2957 chan_name = strdup(channel->name);
2958 del_channel_user(victim, 1);
2959 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2960 free(chan_name);
0f6fe38c 2961 return 1;
d76ed9a9
AS
2962}
2963
2964static int
2965cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2966{
0f6fe38c 2967 struct userData *actor, *uData, *next;
d76ed9a9
AS
2968
2969 actor = GetChannelUser(channel->channel_info, user->handle_info);
2970
2971 if(min_access > max_access)
2972 {
2973 reply("CSMSG_BAD_RANGE", min_access, max_access);
2974 return 0;
2975 }
2976
2977 if((actor->access <= max_access) && !IsHelping(user))
2978 {
2979 reply("CSMSG_NO_ACCESS");
2980 return 0;
2981 }
2982
2983 for(uData = channel->channel_info->users; uData; uData = next)
2984 {
2985 next = uData->next;
2986
2987 if((uData->access >= min_access)
2988 && (uData->access <= max_access)
2989 && match_ircglob(uData->handle->handle, mask))
2990 del_channel_user(uData, 1);
2991 }
2992
2993 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
0f6fe38c 2994 return 1;
d76ed9a9
AS
2995}
2996
2997static CHANSERV_FUNC(cmd_mdelowner)
2998{
2999 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
3000}
3001
3002static CHANSERV_FUNC(cmd_mdelcoowner)
3003{
11d3c79b 3004 return cmd_mdel_user(user, channel, UL_COOWNER, UL_OWNER-1, argv[1], cmd);
d76ed9a9
AS
3005}
3006
4048352e 3007static CHANSERV_FUNC(cmd_mdelmanager)
d76ed9a9 3008{
11d3c79b 3009 return cmd_mdel_user(user, channel, UL_MANAGER, UL_COOWNER-1, argv[1], cmd);
d76ed9a9
AS
3010}
3011
3012static CHANSERV_FUNC(cmd_mdelop)
3013{
11d3c79b 3014 return cmd_mdel_user(user, channel, UL_OP, UL_MANAGER-1, argv[1], cmd);
d76ed9a9
AS
3015}
3016
11d3c79b 3017static CHANSERV_FUNC(cmd_mdelhalfop)
d76ed9a9 3018{
11d3c79b 3019 return cmd_mdel_user(user, channel, UL_HALFOP, UL_OP-1, argv[1], cmd);
d76ed9a9
AS
3020}
3021
11d3c79b 3022static CHANSERV_FUNC(cmd_mdelpeon)
55342ce8 3023{
11d3c79b 3024 return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
55342ce8 3025}
3026
3027
c9bf23fe
AS
3028static CHANSERV_FUNC(cmd_levels)
3029{
3030 struct helpfile_table tbl;
3031 int ii = 0;
3032
3033 tbl.length = 6 + 1; // 6 levels
3034 tbl.width = 4;
3035 tbl.flags = 0;
3036 tbl.contents = calloc(tbl.length,sizeof(tbl.contents[0]));
3037 tbl.contents[0] = calloc(tbl.width,sizeof(tbl.contents[0][0]));
3038 tbl.contents[0][0] = "Level";
3039 tbl.contents[0][1] = "From";
3040 tbl.contents[0][2] = "-";
3041 tbl.contents[0][3] = "To";
3042
3043 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3044 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OWNER));
3045 tbl.contents[ii][1] = msnprintf(4, "%d", UL_OWNER);
7a278540 3046 tbl.contents[ii][2] = msnprintf(2, " ");
c9bf23fe
AS
3047 tbl.contents[ii][3] = msnprintf(1, "");
3048
3049 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3050 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_COOWNER));
3051 tbl.contents[ii][1] = msnprintf(4, "%d", UL_COOWNER);
7a278540 3052 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe
AS
3053 tbl.contents[ii][3] = msnprintf(4, "%d", UL_OWNER-1);
3054
3055 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3056 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_MANAGER));
3057 tbl.contents[ii][1] = msnprintf(4, "%d", UL_MANAGER);
7a278540 3058 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe
AS
3059 tbl.contents[ii][3] = msnprintf(4, "%d", UL_COOWNER-1);
3060
3061 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3062 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OP));
3063 tbl.contents[ii][1] = msnprintf(4, "%d", UL_OP);
7a278540 3064 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe
AS
3065 tbl.contents[ii][3] = msnprintf(4, "%d", UL_MANAGER-1);
3066
3067 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3068 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_HALFOP));
3069 tbl.contents[ii][1] = msnprintf(4, "%d", UL_HALFOP);
7a278540 3070 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe
AS
3071 tbl.contents[ii][3] = msnprintf(4, "%d", UL_OP-1);
3072
3073 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3074 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_PEON));
3075 tbl.contents[ii][1] = msnprintf(4, "%d", UL_PEON);
7a278540 3076 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe
AS
3077 tbl.contents[ii][3] = msnprintf(4, "%d", UL_HALFOP-1);
3078
3079 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3080 return 0;
3081
3082/*
3083 reply("CSMSG_LEVELS_HEADER");
3084 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3085 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3086 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3087 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3088 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3089 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3090 reply("CSMSG_BAR");
3091 */
3092}
3093
c8273589 3094/* trim_lamers.. */
d76ed9a9 3095static int
c092fcad 3096cmd_trim_bans(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned long duration)
d76ed9a9
AS
3097{
3098 struct banData *bData, *next;
3099 char interval[INTERVALLEN];
3100 unsigned int count;
3101 time_t limit;
3102
3103 count = 0;
3104 limit = now - duration;
3105 for(bData = channel->channel_info->bans; bData; bData = next)
3106 {
3107 next = bData->next;
3108
3109 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3110 continue;
3111
3112 del_channel_ban(bData);
3113 count++;
3114 }
3115
3116 intervalString(interval, duration, user->handle_info);
c092fcad 3117 reply("CSMSG_TRIMMED_LAMERS", count, channel->name, interval);
d76ed9a9
AS
3118 return 1;
3119}
3120
3121static int
c092fcad 3122cmd_trim_users(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
d76ed9a9
AS
3123{
3124 struct userData *actor, *uData, *next;
3125 char interval[INTERVALLEN];
3126 unsigned int count;
3127 time_t limit;
3128
3129 actor = GetChannelUser(channel->channel_info, user->handle_info);
3130 if(min_access > max_access)
3131 {
c092fcad 3132 reply("CSMSG_BAD_RANGE", min_access, max_access);
d76ed9a9
AS
3133 return 0;
3134 }
3135
0f6fe38c 3136 if((actor->access <= max_access) && !IsHelping(user))
d76ed9a9 3137 {
c092fcad 3138 reply("CSMSG_NO_ACCESS");
d76ed9a9
AS
3139 return 0;
3140 }
3141
3142 count = 0;
3143 limit = now - duration;
3144 for(uData = channel->channel_info->users; uData; uData = next)
3145 {
3146 next = uData->next;
3147
2f61d1d7 3148 if((uData->seen > limit)
3149 || uData->present
3150 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
d76ed9a9
AS
3151 continue;
3152
3153 if(((uData->access >= min_access) && (uData->access <= max_access))
3154 || (!max_access && (uData->access < actor->access)))
3155 {
3156 del_channel_user(uData, 1);
3157 count++;
3158 }
3159 }
3160
3161 if(!max_access)
3162 {
3163 min_access = 1;
a32da4c7 3164 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
d76ed9a9 3165 }
c092fcad 3166 reply("CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
d76ed9a9
AS
3167 return 1;
3168}
3169
3170static CHANSERV_FUNC(cmd_trim)
3171{
3172 unsigned long duration;
3173 unsigned short min_level, max_level;
2f61d1d7 3174 int vacation;
d76ed9a9
AS
3175
3176 REQUIRE_PARAMS(3);
3177
2f61d1d7 3178 vacation = argc > 3 && !strcmp(argv[3], "vacation");
d76ed9a9
AS
3179 duration = ParseInterval(argv[2]);
3180 if(duration < 60)
3181 {
3182 reply("CSMSG_CANNOT_TRIM");
3183 return 0;
3184 }
3185
c8273589 3186 if(!irccasecmp(argv[1], "lamers"))
d76ed9a9 3187 {
c092fcad 3188 cmd_trim_bans(cmd, user, channel, duration); /* trim_lamers.. */
d76ed9a9
AS
3189 return 1;
3190 }
3191 else if(!irccasecmp(argv[1], "users"))
3192 {
c092fcad 3193 cmd_trim_users(cmd, user, channel, 0, 0, duration, vacation);
d76ed9a9
AS
3194 return 1;
3195 }
3196 else if(parse_level_range(&min_level, &max_level, argv[1]))
3197 {
c092fcad 3198 cmd_trim_users(cmd, user, channel, min_level, max_level, duration, vacation);
d76ed9a9
AS
3199 return 1;
3200 }
3201 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3202 {
c092fcad 3203 cmd_trim_users(cmd, user, channel, min_level, min_level, duration, vacation);
d76ed9a9
AS
3204 return 1;
3205 }
3206 else
3207 {
3208 reply("CSMSG_INVALID_TRIM", argv[1]);
3209 return 0;
3210 }
3211}
3212
3213/* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3214 to the user. cmd_all takes advantage of this. */
3215static CHANSERV_FUNC(cmd_up)
3216{
3217 struct mod_chanmode change;
3218 struct userData *uData;
3219 const char *errmsg;
3220
3221 mod_chanmode_init(&change);
3222 change.argc = 1;
a32da4c7 3223 change.args[0].u.member = GetUserMode(channel, user);
3224 if(!change.args[0].u.member)
d76ed9a9
AS
3225 {
3226 if(argc)
3227 reply("MSG_CHANNEL_ABSENT", channel->name);
3228 return 0;
3229 }
3230
3231 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3232 if(!uData)
3233 {
3234 if(argc)
3235 reply("CSMSG_GODMODE_UP", argv[0]);
3236 return 0;
3237 }
4b6129c0 3238 else if(uData->access >= UL_OP)
d76ed9a9
AS
3239 {
3240 change.args[0].mode = MODE_CHANOP;
3241 errmsg = "CSMSG_ALREADY_OPPED";
3242 }
4b6129c0 3243 else if(uData->access >= UL_HALFOP )
55342ce8 3244 {
3245 change.args[0].mode = MODE_HALFOP;
3246 errmsg = "CSMSG_ALREADY_HALFOPPED";
3247 }
4b6129c0 3248 else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chAutomode] != 'm' ))
d76ed9a9
AS
3249 {
3250 change.args[0].mode = MODE_VOICE;
3251 errmsg = "CSMSG_ALREADY_VOICED";
3252 }
3253 else
3254 {
3255 if(argc)
3256 reply("CSMSG_NO_ACCESS");
3257 return 0;
3258 }
a32da4c7 3259 change.args[0].mode &= ~change.args[0].u.member->modes;
d76ed9a9
AS
3260 if(!change.args[0].mode)
3261 {
3262 if(argc)
3263 reply(errmsg, channel->name);
3264 return 0;
3265 }
3266 modcmd_chanmode_announce(&change);
3267 return 1;
3268}
3269
3270static CHANSERV_FUNC(cmd_down)
3271{
3272 struct mod_chanmode change;
3273
3274 mod_chanmode_init(&change);
3275 change.argc = 1;
a32da4c7 3276 change.args[0].u.member = GetUserMode(channel, user);
3277 if(!change.args[0].u.member)
d76ed9a9
AS
3278 {
3279 if(argc)
3280 reply("MSG_CHANNEL_ABSENT", channel->name);
3281 return 0;
3282 }
3283
a32da4c7 3284 if(!change.args[0].u.member->modes)
d76ed9a9
AS
3285 {
3286 if(argc)
3287 reply("CSMSG_ALREADY_DOWN", channel->name);
3288 return 0;
3289 }
3290
a32da4c7 3291 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
d76ed9a9
AS
3292 modcmd_chanmode_announce(&change);
3293 return 1;
3294}
3295
3296static 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)
3297{
3298 struct userData *cList;
3299
3300 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3301 {
3302 if(IsSuspended(cList->channel)
3303 || IsUserSuspended(cList)
3304 || !GetUserMode(cList->channel->channel, user))
3305 continue;
3306
3307 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3308 }
3309
3310 return 1;
3311}
3312
3313static CHANSERV_FUNC(cmd_upall)
3314{
3315 return cmd_all(CSFUNC_ARGS, cmd_up);
3316}
3317
3318static CHANSERV_FUNC(cmd_downall)
3319{
3320 return cmd_all(CSFUNC_ARGS, cmd_down);
3321}
3322
c092fcad 3323typedef int validate_func_t(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim);
d76ed9a9
AS
3324typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3325
3326static int
3327modify_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)
3328{
3329 unsigned int ii, valid;
3330 struct userNode *victim;
3331 struct mod_chanmode *change;
3332
3333 change = mod_chanmode_alloc(argc - 1);
3334
3335 for(ii=valid=0; ++ii < argc; )
3336 {
3337 if(!(victim = GetUserH(argv[ii])))
3338 continue;
3339 change->args[valid].mode = mode;
a32da4c7 3340 change->args[valid].u.member = GetUserMode(channel, victim);
3341 if(!change->args[valid].u.member)
d76ed9a9 3342 continue;
c092fcad 3343 if(validate && !validate(cmd, user, channel, victim))
d76ed9a9
AS
3344 continue;
3345 valid++;
3346 }
3347
3348 change->argc = valid;
3349 if(valid < (argc-1))
3350 reply("CSMSG_PROCESS_FAILED");
3351 if(valid)
3352 {
3353 modcmd_chanmode_announce(change);
3354 reply(action, channel->name);
3355 }
3356 mod_chanmode_free(change);
3357 return 1;
3358}
3359
3360static CHANSERV_FUNC(cmd_op)
3361{
3362 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3363}
3364
55342ce8 3365static CHANSERV_FUNC(cmd_hop)
3366{
3367 return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS");
3368}
3369
d76ed9a9
AS
3370static CHANSERV_FUNC(cmd_deop)
3371{
3372 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3373}
3374
55342ce8 3375static CHANSERV_FUNC(cmd_dehop)
3376{
3377 return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS");
3378}
3379
d76ed9a9
AS
3380static CHANSERV_FUNC(cmd_voice)
3381{
3382 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3383}
3384
3385static CHANSERV_FUNC(cmd_devoice)
3386{
3387 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3388}
3389
3390static int
697f4c9a 3391bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
d76ed9a9
AS
3392{
3393 unsigned int ii;
3394
3395 if(victimCount)
3396 *victimCount = 0;
3397 for(ii=0; ii<channel->members.used; ii++)
3398 {
3399 struct modeNode *mn = channel->members.list[ii];
3400
3401 if(IsService(mn->user))
3402 continue;
3403
2f61d1d7 3404 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9
AS
3405 continue;
3406
ff5f1ab2 3407 if(protect_user(mn->user, user, channel->channel_info, false))
d76ed9a9
AS
3408 return 1;
3409
3410 if(victims)
3411 victims[(*victimCount)++] = mn;
3412 }
3413 return 0;
3414}
3415
3416static int
3417eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3418{
3419 struct userNode *victim;
3420 struct modeNode **victims;
3421 unsigned int offset, n, victimCount, duration = 0;
d3347099 3422 char *reason = "Bye.", *ban, *name;
d76ed9a9
AS
3423 char interval[INTERVALLEN];
3424
c8273589 3425 offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
d76ed9a9
AS
3426 REQUIRE_PARAMS(offset);
3427 if(argc > offset)
3428 {
3429 reason = unsplit_string(argv + offset, argc - offset, NULL);
3430 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3431 {
3432 /* Truncate the reason to a length of TOPICLEN, as
3433 the ircd does; however, leave room for an ellipsis
3434 and the kicker's nick. */
3435 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3436 }
3437 }
3438
3439 if((victim = GetUserH(argv[1])))
3440 {
3441 victims = alloca(sizeof(victims[0]));
3442 victims[0] = GetUserMode(channel, victim);
3443 /* XXX: The comparison with ACTION_KICK is just because all
3444 * other actions can work on users outside the channel, and we
3445 * want to allow those (e.g. unbans) in that case. If we add
3446 * some other ejection action for in-channel users, change
3447 * this too. */
3448 victimCount = victims[0] ? 1 : 0;
3449
3450 if(IsService(victim))
3451 {
35caf917
AS
3452 if(cmd)
3453 reply("MSG_SERVICE_IMMUNE", victim->nick);
d76ed9a9
AS
3454 return 0;
3455 }
3456
3457 if((action == ACTION_KICK) && !victimCount)
3458 {
35caf917
AS
3459 if(cmd)
3460 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
d76ed9a9
AS
3461 return 0;
3462 }
3463
ff5f1ab2 3464 if(protect_user(victim, user, channel->channel_info, false))
d76ed9a9 3465 {
35caf917
AS
3466 // This translates to send_message(user, cmd->parent->bot, ...)
3467 // if user is x3 (ctcp action) cmd is null and segfault.
3468 if(cmd)
3469 reply("CSMSG_USER_PROTECTED", victim->nick);
d76ed9a9
AS
3470 return 0;
3471 }
3472
d3347099 3473 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
d76ed9a9
AS
3474 name = victim->nick;
3475 }
3476 else
3477 {
3478 if(!is_ircmask(argv[1]))
3479 {
35caf917
AS
3480 if(cmd)
3481 reply("MSG_NICK_UNKNOWN", argv[1]);
d76ed9a9
AS
3482 return 0;
3483 }
3484
3485 victims = alloca(sizeof(victims[0]) * channel->members.used);
3486
3487 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3488 {
35caf917
AS
3489 if(cmd)
3490 reply("CSMSG_MASK_PROTECTED", argv[1]);
d76ed9a9
AS
3491 return 0;
3492 }
d1a65675
AS
3493/* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3494// if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3495 And, ^^^^^^^^^ BAH!
3496 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3497 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3498 some creativity, but its not x3's job to be the ban censor anyway. */
3499 if(is_overmask(argv[1]))
d76ed9a9 3500 {
35caf917
AS
3501 if(cmd)
3502 reply("CSMSG_LAME_MASK", argv[1]);
d76ed9a9
AS
3503 return 0;
3504 }
3505
3506 if((action == ACTION_KICK) && (victimCount == 0))
3507 {
35caf917
AS
3508 if(cmd)
3509 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
d76ed9a9
AS
3510 return 0;
3511 }
3512
3513 name = ban = strdup(argv[1]);
3514 }
3515
3516 /* Truncate the ban in place if necessary; we must ensure
3517 that 'ban' is a valid ban mask before sanitizing it. */
3518 sanitize_ircmask(ban);
3519
c8273589 3520 if(action & ACTION_ADD_LAMER)
d76ed9a9
AS
3521 {
3522 struct banData *bData, *next;
3523
c8273589 3524 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
d76ed9a9 3525 {
35caf917
AS
3526 if(cmd)
3527 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
d76ed9a9
AS
3528 free(ban);
3529 return 0;
3530 }
3531
c8273589 3532 if(action & ACTION_ADD_TIMED_LAMER)
d76ed9a9
AS
3533 {
3534 duration = ParseInterval(argv[2]);
3535
3536 if(duration < 15)
3537 {
35caf917
AS
3538 if(cmd)
3539 reply("CSMSG_DURATION_TOO_LOW");
d76ed9a9
AS
3540 free(ban);
3541 return 0;
3542 }
3543 else if(duration > (86400 * 365 * 2))
3544 {
35caf917
AS
3545 if(cmd)
3546 reply("CSMSG_DURATION_TOO_HIGH");
d76ed9a9
AS
3547 free(ban);
3548 return 0;
3549 }
3550 }
3551
c8273589 3552 /* lamers... */
d76ed9a9
AS
3553 for(bData = channel->channel_info->bans; bData; bData = next)
3554 {
3555 if(match_ircglobs(bData->mask, ban))
3556 {
3557 int exact = !irccasecmp(bData->mask, ban);
3558
3559 /* The ban is redundant; there is already a ban
3560 with the same effect in place. */
3561 if(exact)
3562 {
3563 if(bData->reason)
3564 free(bData->reason);
3565 bData->reason = strdup(reason);
3566 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3567 if(cmd)
3568 reply("CSMSG_REASON_CHANGE", ban);
3569 if(!bData->expires)
3570 goto post_add_ban;
3571 }
3572 if(exact && bData->expires)
3573 {
3574 int reset = 0;
3575
3576 /* If the ban matches an existing one exactly,
3577 extend the expiration time if the provided
3578 duration is longer. */
3579 if(duration && ((time_t)(now + duration) > bData->expires))
3580 {
3581 bData->expires = now + duration;
3582 reset = 1;
3583 }
3584 else if(!duration)
3585 {
3586 bData->expires = 0;
3587 reset = 1;
3588 }
3589
3590 if(reset)
3591 {
3592 /* Delete the expiration timeq entry and
3593 requeue if necessary. */
3594 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3595
3596 if(bData->expires)
3597 timeq_add(bData->expires, expire_ban, bData);
3598
3599 if(!cmd)
3600 {
35caf917 3601 /* automated kickban, dont reply */
d76ed9a9
AS
3602 }
3603 else if(duration)
c8273589 3604 reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
d76ed9a9 3605 else
c8273589 3606 reply("CSMSG_LAMER_ADDED", name, channel->name);
d76ed9a9
AS
3607
3608 goto post_add_ban;
3609 }
3610 }
3611 if(cmd)
c8273589 3612 reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
d76ed9a9
AS
3613
3614 free(ban);
3615 return 0;
3616 }
3617
3618 next = bData->next;
3619 if(match_ircglobs(ban, bData->mask))
3620 {
3621 /* The ban we are adding makes previously existing
3622 bans redundant; silently remove them. */
3623 del_channel_ban(bData);
3624 }
3625 }
3626
3627 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);
3628 free(ban);
3629 name = ban = strdup(bData->mask);
3630 }
3631 else if(ban)
3632 {
19fe6b99 3633 /* WHAT DOES THIS DO?? -Rubin */
d76ed9a9
AS
3634 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3635 {
3636 extern const char *hidden_host_suffix;
3637 const char *old_name = chanserv_conf.old_ban_names->list[n];
3638 char *new_mask;
3639 unsigned int l1, l2;
3640
3641 l1 = strlen(ban);
3642 l2 = strlen(old_name);
3643 if(l2+2 > l1)
3644 continue;
3645 if(irccasecmp(ban + l1 - l2, old_name))
3646 continue;
3647 new_mask = malloc(MAXLEN);
a32da4c7 3648 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
d76ed9a9
AS
3649 free(ban);
3650 name = ban = new_mask;
3651 }
3652 }
3653
3654 post_add_ban:
3655 if(action & ACTION_BAN)
3656 {
3657 unsigned int exists;
3658 struct mod_chanmode *change;
3659
3660 if(channel->banlist.used >= MAXBANS)
3661 {
3662 if(cmd)
3663 reply("CSMSG_BANLIST_FULL", channel->name);
3664 free(ban);
3665 return 0;
3666 }
3667
3668 exists = ChannelBanExists(channel, ban);
3669 change = mod_chanmode_alloc(victimCount + 1);
3670 for(n = 0; n < victimCount; ++n)
3671 {
55342ce8 3672 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
a32da4c7 3673 change->args[n].u.member = victims[n];
d76ed9a9
AS
3674 }
3675 if(!exists)
3676 {
3677 change->args[n].mode = MODE_BAN;
a32da4c7 3678 change->args[n++].u.hostmask = ban;
d76ed9a9
AS
3679 }
3680 change->argc = n;
3681 if(cmd)
3682 modcmd_chanmode_announce(change);
3683 else
3684 mod_chanmode_announce(chanserv, channel, change);
3685 mod_chanmode_free(change);
3686
3687 if(exists && (action == ACTION_BAN))
3688 {
3689 if(cmd)
3690 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3691 free(ban);
3692 return 0;
3693 }
3694 }
3695
ff5f1ab2
AS
3696 if(action & ACTION_ADD_LAMER)
3697 {
3698 char kick_reason[MAXLEN];
3699 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3700
3701 for(n = 0; n < victimCount; n++) {
3702 if(!protect_user(victims[n]->user, user, channel->channel_info, true)) {
3703 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3704 }
3705 }
3706 }
3707 else if(action & ACTION_KICK)
d76ed9a9
AS
3708 {
3709 char kick_reason[MAXLEN];
3710 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3711
ff5f1ab2 3712 for(n = 0; n < victimCount; n++) {
d76ed9a9 3713 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
ff5f1ab2 3714 }
d76ed9a9
AS
3715 }
3716
3717 if(!cmd)
3718 {
3719 /* No response, since it was automated. */
3720 }
c8273589 3721 else if(action & ACTION_ADD_LAMER)
d76ed9a9
AS
3722 {
3723 if(duration)
c8273589 3724 reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
d76ed9a9 3725 else
c8273589 3726 reply("CSMSG_LAMER_ADDED", name, channel->name);
d76ed9a9
AS
3727 }
3728 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3729 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3730 else if(action & ACTION_BAN)
3731 reply("CSMSG_BAN_DONE", name, channel->name);
3732 else if(action & ACTION_KICK && victimCount)
3733 reply("CSMSG_KICK_DONE", name, channel->name);
3734
3735 free(ban);
3736 return 1;
3737}
3738
3739static CHANSERV_FUNC(cmd_kickban)
3740{
3741 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3742}
3743
3744static CHANSERV_FUNC(cmd_kick)
3745{
3746 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3747}
3748
3749static CHANSERV_FUNC(cmd_ban)
3750{
3751 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3752}
3753
c8273589 3754static CHANSERV_FUNC(cmd_addlamer)
d76ed9a9 3755{
c8273589 3756 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER);
d76ed9a9
AS
3757}
3758
c8273589 3759static CHANSERV_FUNC(cmd_addtimedlamer)
d76ed9a9 3760{
c8273589 3761 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER);
d76ed9a9
AS
3762}
3763
3764static struct mod_chanmode *
3765find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3766{
3767 struct mod_chanmode *change;
3768 unsigned char *match;
3769 unsigned int ii, count;
3770
3771 match = alloca(bans->used);
3772 if(actee)
3773 {
3774 for(ii = count = 0; ii < bans->used; ++ii)
3775 {
2f61d1d7 3776 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3777 MATCH_USENICK | MATCH_VISIBLE);
d76ed9a9
AS
3778 if(match[ii])
3779 count++;
3780 }
3781 }
3782 else
3783 {
3784 for(ii = count = 0; ii < bans->used; ++ii)
3785 {
3786 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3787 if(match[ii])
3788 count++;
3789 }
3790 }
3791 if(!count)
3792 return NULL;
3793 change = mod_chanmode_alloc(count);
3794 for(ii = count = 0; ii < bans->used; ++ii)
3795 {
3796 if(!match[ii])
3797 continue;
3798 change->args[count].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 3799 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
d76ed9a9 3800 }
ec1a68c8 3801 assert(count == change->argc);
d76ed9a9
AS
3802 return change;
3803}
3804
7153d2c7
AS
3805void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
3806{
3807 unsigned int jj, ii, count;
3808 struct banNode *bn;
3809 struct chanData *channel;
3810 time_t bantimeout;
3811 struct mod_chanmode *change;
3812
3813 log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
3814 /* Walk through every channel */
3815 for(channel = channelList; channel; channel = channel->next) {
3816 switch(channel->chOpts[chBanTimeout])
3817 {
3818 default: case '0': continue; /* Dont remove bans in this chan */
3819 case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */
3820 case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */
3821 case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */
3822 case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */
3823 case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
3824 }
3825 count = 0;
3826 /* First find out how many bans were going to unset */
3827 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3828 if(channel->channel->banlist.list[jj]->set < bantimeout)
3829 count++;
3830 }
3831 if(count > 0) {
3832 /* At least one ban, so setup a removal */
3833 change = mod_chanmode_alloc(count);
3834 ii = 0;
3835 /* Walk over every ban in this channel.. */
3836 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3837 bn = channel->channel->banlist.list[jj];
3838 if (bn->set < bantimeout) {
3839 log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
3840
3841 /* Add this ban to the mode change */
3842 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3843 change->args[ii].u.hostmask = strdup(bn->ban);
3844 ii++;
3845 /* Pull this ban out of the list */
3846 banList_remove(&(channel->channel->banlist), bn);
3847 jj--;
3848 free(bn);
3849 }
3850 }
3851 /* Send the modes to IRC */
3852 mod_chanmode_announce(chanserv, channel->channel, change);
3853
3854 /* free memory from strdup above */
3855 for(ii = 0; ii < count; ++ii)
3856 free((char*)change->args[ii].u.hostmask);
3857
3858 mod_chanmode_free(change);
3859 }
3860 }
3861 /* Set this function to run again */
3862 if(chanserv_conf.ban_timeout_frequency)
3863 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
3864}
3865
3866
d76ed9a9
AS
3867static int
3868unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3869{
3870 struct userNode *actee;
3871 char *mask = NULL;
3872 int acted = 0;
3873
3874 REQUIRE_PARAMS(2);
3875
3876 /* may want to allow a comma delimited list of users... */
3877 if(!(actee = GetUserH(argv[1])))
3878 {
0f6fe38c 3879 if(!is_ircmask(argv[1]))
3880 {
3881 reply("MSG_NICK_UNKNOWN", argv[1]);
3882 return 0;
ff3b058a 3883 }
0f6fe38c 3884
3885 mask = strdup(argv[1]);
d76ed9a9
AS
3886 }
3887
3888 /* We don't sanitize the mask here because ircu
3889 doesn't do it. */
3890 if(action & ACTION_UNBAN)
3891 {
3892 struct mod_chanmode *change;
3893 change = find_matching_bans(&channel->banlist, actee, mask);
3894 if(change)
3895 {
ec1a68c8 3896 unsigned int ii;
3897
aa222db2 3898 modcmd_chanmode_announce(change);
ec1a68c8 3899 for(ii = 0; ii < change->argc; ++ii)
3900 free((char*)change->args[ii].u.hostmask);
d76ed9a9
AS
3901 mod_chanmode_free(change);
3902 acted = 1;
3903 }
3904 }
3905
c8273589 3906 if(action & ACTION_DEL_LAMER)
d76ed9a9
AS
3907 {
3908 struct banData *ban, *next;
3909
c8273589 3910 ban = channel->channel_info->bans; /* lamers */
d76ed9a9
AS
3911 while(ban)
3912 {
3913 if(actee)
2f61d1d7 3914 for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE);
d76ed9a9
AS
3915 ban = ban->next);
3916 else
3917 for( ; ban && !match_ircglobs(mask, ban->mask);
3918 ban = ban->next);
3919 if(!ban)
3920 break;
3921 next = ban->next;
3922 del_channel_ban(ban);
3923 ban = next;
3924 acted = 1;
3925 }
3926 }
3927
3928 if(!acted)
3929 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3930 else
3931 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3932 if(mask)
3933 free(mask);
3934 return 1;
3935}
3936
3937static CHANSERV_FUNC(cmd_unban)
3938{
3939 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3940}
3941
c8273589 3942static CHANSERV_FUNC(cmd_dellamer)
d76ed9a9
AS
3943{
3944 /* it doesn't necessarily have to remove the channel ban - may want
3945 to make that an option. */
c8273589 3946 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_LAMER);
d76ed9a9
AS
3947}
3948
3949static CHANSERV_FUNC(cmd_unbanme)
3950{
3951 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3952 long flags = ACTION_UNBAN;
3953
3954 /* remove permanent bans if the user has the proper access. */
4048352e 3955 if(uData->access >= UL_MANAGER)
c8273589 3956 flags |= ACTION_DEL_LAMER;
d76ed9a9
AS
3957
3958 argv[1] = user->nick;
3959 return unban_user(user, channel, 2, argv, cmd, flags);
3960}
3961
3962static CHANSERV_FUNC(cmd_unbanall)
3963{
3964 struct mod_chanmode *change;
3965 unsigned int ii;
3966
3967 if(!channel->banlist.used)
3968 {
3969 reply("CSMSG_NO_BANS", channel->name);
3970 return 0;
3971 }
3972
3973 change = mod_chanmode_alloc(channel->banlist.used);
3974 for(ii=0; ii<channel->banlist.used; ii++)
3975 {
3976 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 3977 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
d76ed9a9
AS
3978 }
3979 modcmd_chanmode_announce(change);
ec1a68c8 3980 for(ii = 0; ii < change->argc; ++ii)
3981 free((char*)change->args[ii].u.hostmask);
d76ed9a9
AS
3982 mod_chanmode_free(change);
3983 reply("CSMSG_BANS_REMOVED", channel->name);
3984 return 1;
3985}
3986
3987static CHANSERV_FUNC(cmd_open)
3988{
3989 struct mod_chanmode *change;
ec1a68c8 3990 unsigned int ii;
d76ed9a9
AS
3991
3992 change = find_matching_bans(&channel->banlist, user, NULL);
3993 if(!change)
3994 change = mod_chanmode_alloc(0);
3995 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3996 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3997 && channel->channel_info->modes.modes_set)
3998 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3999 modcmd_chanmode_announce(change);
4000 reply("CSMSG_CHANNEL_OPENED", channel->name);
ec1a68c8 4001 for(ii = 0; ii < change->argc; ++ii)
4002 free((char*)change->args[ii].u.hostmask);
d76ed9a9
AS
4003 mod_chanmode_free(change);
4004 return 1;
4005}
4006
4007static CHANSERV_FUNC(cmd_myaccess)
4008{
4009 static struct string_buffer sbuf;
4010 struct handle_info *target_handle;
4011 struct userData *uData;
4012
4013 if(argc < 2)
4014 target_handle = user->handle_info;
23b7ec6e
AS
4015 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
4016 return 0;
4017 else if(!IsHelping(user) && target_handle != user->handle_info)
d76ed9a9
AS
4018 {
4019 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
4020 return 0;
4021 }
d76ed9a9
AS
4022 if(!target_handle->channels)
4023 {
4024 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
4025 return 1;
4026 }
4027
4028 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
4029 for(uData = target_handle->channels; uData; uData = uData->u_next)
4030 {
4031 struct chanData *cData = uData->channel;
4032
4033 if(uData->access > UL_OWNER)
4034 continue;
4035 if(IsProtected(cData)
4036 && (target_handle != user->handle_info)
4037 && !GetTrueChannelAccess(cData, user->handle_info))
4038 continue;
4039 sbuf.used = 0;
4040 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
c8ca69a0 4041 if(uData->flags == USER_AUTO_OP)
d76ed9a9
AS
4042 string_buffer_append(&sbuf, ',');
4043 if(IsUserSuspended(uData))
4044 string_buffer_append(&sbuf, 's');
4045 if(IsUserAutoOp(uData))
4046 {
4b6129c0 4047 if(uData->access >= UL_OP )
d76ed9a9 4048 string_buffer_append(&sbuf, 'o');
4b6129c0 4049 else if(uData->access >= UL_HALFOP )
55342ce8 4050 string_buffer_append(&sbuf, 'h');
4b6129c0 4051 else if(uData->access >= UL_PEON )
d76ed9a9
AS
4052 string_buffer_append(&sbuf, 'v');
4053 }
4054 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
4055 string_buffer_append(&sbuf, 'i');
cd25f2e9 4056 if(IsUserAutoJoin(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
4057 string_buffer_append(&sbuf, 'j');
d76ed9a9
AS
4058 if(uData->info)
4059 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
4060 else
4061 string_buffer_append_string(&sbuf, ")]");
4062 string_buffer_append(&sbuf, '\0');
a32da4c7 4063 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
d76ed9a9
AS
4064 }
4065
4066 return 1;
4067}
4068
4069static CHANSERV_FUNC(cmd_access)
4070{
4071 struct userNode *target;
4072 struct handle_info *target_handle;
4073 struct userData *uData;
4074 int helping;
4075 char prefix[MAXLEN];
4076
4077 if(argc < 2)
4078 {
4079 target = user;
4080 target_handle = target->handle_info;
4081 }
4082 else if((target = GetUserH(argv[1])))
4083 {
4084 target_handle = target->handle_info;
4085 }
4086 else if(argv[1][0] == '*')
4087 {
4088 if(!(target_handle = get_handle_info(argv[1]+1)))
4089 {
4090 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
4091 return 0;
4092 }
4093 }
4094 else
4095 {
4096 reply("MSG_NICK_UNKNOWN", argv[1]);
4097 return 0;
4098 }
4099
4100 assert(target || target_handle);
4101
4102 if(target == chanserv)
4103 {
4104 reply("CSMSG_IS_CHANSERV");
4105 return 1;
4106 }
4107
4108 if(!target_handle)
4109 {
4110 if(IsOper(target))
4111 {
4112 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4113 return 0;
4114 }
4115 if(target != user)
4116 {
4117 reply("MSG_USER_AUTHENTICATE", target->nick);
4118 return 0;
4119 }
4120 reply("MSG_AUTHENTICATE");
4121 return 0;
4122 }
4123
4124 if(target)
4125 {
4126 const char *epithet = NULL, *type = NULL;
4127 if(IsOper(target))
4128 {
4129 epithet = chanserv_conf.irc_operator_epithet;
0f6fe38c 4130 type = "IRCOp";
d76ed9a9
AS
4131 }
4132 else if(IsNetworkHelper(target))
4133 {
4134 epithet = chanserv_conf.network_helper_epithet;
0f6fe38c 4135 type = "network helper";
d76ed9a9
AS
4136 }
4137 else if(IsSupportHelper(target))
4138 {
4139 epithet = chanserv_conf.support_helper_epithet;
0f6fe38c 4140 type = "support helper";
d76ed9a9
AS
4141 }
4142 if(epithet)
4143 {
4144 if(target_handle->epithet)
4145 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4146 else if(epithet)
4147 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4148 }
4149 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4150 }
4151 else
4152 {
4153 sprintf(prefix, "%s", target_handle->handle);
4154 }
4155
4156 if(!channel->channel_info)
4157 {
4158 reply("CSMSG_NOT_REGISTERED", channel->name);
4159 return 1;
4160 }
4161
4162 helping = HANDLE_FLAGGED(target_handle, HELPING)
4163 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4164 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4165 {
de9510bc 4166 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, user_level_name_from_level(uData->access), uData->access, channel->name);
d76ed9a9
AS
4167 /* To prevent possible information leaks, only show infolines
4168 * if the requestor is in the channel or it's their own
4169 * handle. */
4170 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4171 {
4172 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4173 }
4174 /* Likewise, only say it's suspended if the user has active
4175 * access in that channel or it's their own entry. */
4176 if(IsUserSuspended(uData)
4177 && (GetChannelUser(channel->channel_info, user->handle_info)
4178 || (user->handle_info == uData->handle)))
4179 {
4180 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4181 }
4182 }
4183 else
4184 {
4185 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4186 }
4187
4188 return 1;
4189}
4190
23475fc6 4191/* This is never used...
d76ed9a9
AS
4192static void
4193zoot_list(struct listData *list)
4194{
4195 struct userData *uData;
4196 unsigned int start, curr, highest, lowest;
4197 struct helpfile_table tmp_table;
4198 const char **temp, *msg;
4199
4200 if(list->table.length == 1)
4201 {
4202 if(list->search)
a915f7d4 4203 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 4204 else
a915f7d4 4205 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
AS
4206 msg = user_find_message(list->user, "MSG_NONE");
4207 send_message_type(4, list->user, list->bot, " %s", msg);
4208 }
4209 tmp_table.width = list->table.width;
4210 tmp_table.flags = list->table.flags;
4211 list->table.contents[0][0] = " ";
4212 highest = list->highest;
4213 if(list->lowest != 0)
4214 lowest = list->lowest;
4215 else if(highest < 100)
4216 lowest = 1;
4217 else
4218 lowest = highest - 100;
4219 for(start = curr = 1; curr < list->table.length; )
4220 {
4221 uData = list->users[curr-1];
4222 list->table.contents[curr++][0] = " ";
4223 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4224 {
4225 if(list->search)
a915f7d4 4226 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 4227 else
a915f7d4 4228 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
AS
4229 temp = list->table.contents[--start];
4230 list->table.contents[start] = list->table.contents[0];
4231 tmp_table.contents = list->table.contents + start;
4232 tmp_table.length = curr - start;
4233 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4234 list->table.contents[start] = temp;
4235 start = curr;
4236 highest = lowest - 1;
4237 lowest = (highest < 100) ? 0 : (highest - 99);
4238 }
4239 }
4240}
23475fc6 4241*/
d76ed9a9
AS
4242
4243static void
338a82b5 4244normal_list(struct listData *list)
d76ed9a9
AS
4245{
4246 const char *msg;
4247 if(list->search)
338a82b5 4248 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
d76ed9a9 4249 else
338a82b5 4250 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
d76ed9a9
AS
4251 if(list->table.length == 1)
4252 {
4253 msg = user_find_message(list->user, "MSG_NONE");
4254 send_message_type(4, list->user, list->bot, " %s", msg);
4255 }
8e11460f
AS
4256 else
4257 table_send(list->bot, list->user->nick, 0, NULL, list->table);
d76ed9a9
AS
4258}
4259
338a82b5
AS
4260/* if these need changed, uncomment and customize
4261static void
4262clean_list(struct listData *list)
4263{
4264 const char *msg;
4265 if(list->search)
4266 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4267 else
4268 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4269 if(list->table.length == 1)
4270 {
4271 msg = user_find_message(list->user, "MSG_NONE");
4272 send_message_type(4, list->user, list->bot, " %s", msg);
4273 }
4274 else
4275 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4276}
4277
4278static void
4279advanced_list(struct listData *list)
4280{
4281 const char *msg;
4282 if(list->search)
4283 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4284 else
4285 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4286 if(list->table.length == 1)
4287 {
4288 msg = user_find_message(list->user, "MSG_NONE");
4289 send_message_type(4, list->user, list->bot, " %s", msg);
4290 }
4291 else
4292 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4293}
4294
d9896a83
AS
4295static void
4296classic_list(struct listData *list)
4297{
4298 const char *msg;
4299 if(list->search)
4300 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4301 else
4302 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4303 if(list->table.length == 1)
4304 {
4305 msg = user_find_message(list->user, "MSG_NONE");
4306 send_message_type(4, list->user, list->bot, " %s", msg);
4307 }
4308 else
4309 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4310}
338a82b5
AS
4311*/
4312
d76ed9a9
AS
4313static int
4314userData_access_comp(const void *arg_a, const void *arg_b)
4315{
4316 const struct userData *a = *(struct userData**)arg_a;
4317 const struct userData *b = *(struct userData**)arg_b;
4318 int res;
4319 if(a->access != b->access)
4320 res = b->access - a->access;
4321 else
4322 res = irccasecmp(a->handle->handle, b->handle->handle);
4323 return res;
4324}
4325
4326static int
4327cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4328{
4329 void (*send_list)(struct listData *);
4330 struct userData *uData;
4331 struct listData lData;
4332 unsigned int matches;
4333 const char **ary;
338a82b5
AS
4334 int i = 0;
4335 int seen_index;
d76ed9a9
AS
4336
4337 lData.user = user;
4338 lData.bot = cmd->parent->bot;
4339 lData.channel = channel;
4340 lData.lowest = lowest;
4341 lData.highest = highest;
4342 lData.search = (argc > 1) ? argv[1] : NULL;
338a82b5 4343 send_list = normal_list;
e6892204 4344 /* What does the following line do exactly?? */
8e11460f 4345 /*(void)zoot_list; ** since it doesn't show user levels */
d76ed9a9 4346
338a82b5 4347 /*
d76ed9a9
AS
4348 if(user->handle_info)
4349 {
338a82b5
AS
4350 switch(user->handle_info->userlist_style)
4351 {
4352 case HI_STYLE_CLEAN:
4353 send_list = clean_list;
4354 break;
4355 case HI_STYLE_ADVANCED:
4356 send_list = advanced_list;
4357 break;
d9896a83
AS
4358 case HI_STYLE_CLASSIC:
4359 send_list = classic_list;
4360 break;
338a82b5
AS
4361 case HI_STYLE_NORMAL:
4362 default:
4363 send_list = normal_list;
4364 break;
4365 }
d76ed9a9 4366 }
e6892204 4367 */
338a82b5 4368 send_list = normal_list;
d76ed9a9
AS
4369
4370 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4371 matches = 0;
4372 for(uData = channel->channel_info->users; uData; uData = uData->next)
4373 {
4374 if((uData->access < lowest)
4375 || (uData->access > highest)
4376 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4377 continue;
4378 lData.users[matches++] = uData;
4379 }
4380 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4381
4382 lData.table.length = matches+1;
d76ed9a9
AS
4383 lData.table.flags = TABLE_NO_FREE;
4384 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
338a82b5 4385
3498a411 4386 if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
fd20b142 4387 lData.table.width = 6; /* with level = 6 */
338a82b5 4388 else
fd20b142 4389 lData.table.width = 5; /* without = 5 */
d76ed9a9
AS
4390 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4391 lData.table.contents[0] = ary;
d9896a83
AS
4392 if(user->handle_info) {
4393 switch(user->handle_info->userlist_style) {
4394 case HI_STYLE_CLASSIC:
4395 ary[i++] = "Level";
4396 break;
4397 case HI_STYLE_ADVANCED:
4398 ary[i++] = "Access";
4399 ary[i++] = "Level";
4400 break;
4401 case HI_STYLE_CLEAN:
4402 ary[i++] = "Access";
4403 break;
4404 case HI_STYLE_NORMAL:
4405 default:
4406 ary[i++] = "Access";
4407 break;
4408 }
4409 }
4410 else {
4411 ary[i++] = "Access";
4412 }
338a82b5
AS
4413 ary[i++] = "Account";
4414 ary[i] = "Last Seen";
4415 seen_index = i++;
4416 ary[i++] = "Status";
fd20b142 4417 ary[i++] = "Expiry";
d76ed9a9
AS
4418 for(matches = 1; matches < lData.table.length; ++matches)
4419 {
4420 struct userData *uData = lData.users[matches-1];
4421 char seen[INTERVALLEN];
4422
338a82b5 4423 i = 0;
d76ed9a9
AS
4424 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4425 lData.table.contents[matches] = ary;
d9896a83
AS
4426 if(user->handle_info) {
4427 switch(user->handle_info->userlist_style) {
4428 case HI_STYLE_CLASSIC:
4429 ary[i++] = strtab(uData->access);
4430 break;
4431 case HI_STYLE_ADVANCED:
4432 ary[i++] = user_level_name_from_level(uData->access);
4433 ary[i++] = strtab(uData->access);
4434 break;
4435 case HI_STYLE_CLEAN:
4436 ary[i++] = user_level_name_from_level(uData->access);
4437 break;
4438 case HI_STYLE_NORMAL:
4439 default:
4440 ary[i++] = user_level_name_from_level(uData->access);
4441 break;
4442 }
4443 }
4444 else {
4445 ary[i++] = user_level_name_from_level(uData->access);
4446 }
338a82b5 4447 ary[i++] = uData->handle->handle;
d76ed9a9 4448 if(uData->present)
338a82b5 4449 ary[i] = "Here";
d76ed9a9 4450 else if(!uData->seen)
338a82b5 4451 ary[i] = "Never";
d76ed9a9 4452 else
338a82b5
AS
4453 ary[i] = intervalString(seen, now - uData->seen, user->handle_info);
4454 ary[i] = strdup(ary[i]);
4455 i++;
d76ed9a9 4456 if(IsUserSuspended(uData))
338a82b5 4457 ary[i++] = "Suspended";
d76ed9a9 4458 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
338a82b5 4459 ary[i++] = "Vacation";
d76ed9a9 4460 else
338a82b5 4461 ary[i++] = "Normal";
fd20b142 4462
dfaa28a4 4463 if ((uData->accessexpiry > 0) || (uData->clvlexpiry > 0)) {
fd20b142 4464 char delay[INTERVALLEN];
4465 time_t diff;
4466
dfaa28a4 4467 if (uData->accessexpiry > 0) {
4468 diff = uData->accessexpiry - now;
4469 intervalString(delay, diff, user->handle_info);
4470 } else {
4471 diff = uData->clvlexpiry - now;
4472 intervalString(delay, diff, user->handle_info);
4473 }
fd20b142 4474 ary[i++] = delay;
4475 } else
4476 ary[i++] = "Never";
d76ed9a9 4477 }
fd20b142 4478
d76ed9a9
AS
4479 send_list(&lData);
4480 for(matches = 1; matches < lData.table.length; ++matches)
4481 {
338a82b5
AS
4482 /* Free strdup above */
4483 free((char*)lData.table.contents[matches][seen_index]);
d76ed9a9
AS
4484 free(lData.table.contents[matches]);
4485 }
4486 free(lData.table.contents[0]);
4487 free(lData.table.contents);
4488 return 1;
4489}
4490
1d957482
AS
4491/* Remove this now that debugging is over? or improve it for
4492 * users? Would it be better tied into USERS somehow? -Rubin */
ac3bdc8d
AS
4493static CHANSERV_FUNC(cmd_pending)
4494{
4495 struct adduserPending *ap;
1d957482 4496 reply("CSMSG_ADDUSER_PENDING_HEADER");
cde20a3f
AS
4497 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4498 reply("CSMSG_BAR");
ac3bdc8d
AS
4499 for(ap = adduser_pendings;ap;ap = ap->next)
4500 reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
1d957482 4501 reply("CSMSG_ADDUSER_PENDING_FOOTER");
ac3bdc8d
AS
4502 return 1;
4503}
4504
d76ed9a9
AS
4505static CHANSERV_FUNC(cmd_users)
4506{
4507 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4508}
4509
4510static CHANSERV_FUNC(cmd_wlist)
4511{
4512 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4513}
4514
4515static CHANSERV_FUNC(cmd_clist)
4516{
4517 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4518}
4519
4520static CHANSERV_FUNC(cmd_mlist)
4521{
4048352e 4522 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
d76ed9a9
AS
4523}
4524
4525static CHANSERV_FUNC(cmd_olist)
4526{
4048352e 4527 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
d76ed9a9
AS
4528}
4529
55342ce8 4530static CHANSERV_FUNC(cmd_hlist)
4531{
4532 return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1);
4533}
4534
d76ed9a9
AS
4535static CHANSERV_FUNC(cmd_plist)
4536{
55342ce8 4537 return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1);
d76ed9a9
AS
4538}
4539
c8273589 4540static CHANSERV_FUNC(cmd_lamers)
d76ed9a9
AS
4541{
4542 struct helpfile_table tbl;
0f6fe38c 4543 unsigned int matches = 0, timed = 0, ii;
d76ed9a9
AS
4544 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4545 const char *msg_never, *triggered, *expires;
c8273589 4546 struct banData *ban, **bans; /* lamers */
d76ed9a9 4547
0f6fe38c 4548 if(argc > 1)
4549 search = argv[1];
4550 else
d76ed9a9
AS
4551 search = NULL;
4552
c8273589
AS
4553 reply("CSMSG_LAMERS_HEADER", channel->name);
4554 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
d76ed9a9 4555
c8273589 4556 /* lamers */
d76ed9a9
AS
4557 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4558 {
0f6fe38c 4559 if(search && !match_ircglobs(search, ban->mask))
d76ed9a9
AS
4560 continue;
4561 bans[matches++] = ban;
4562 if(ban->expires)
4563 timed = 1;
4564 }
4565
4566 tbl.length = matches + 1;
4567 tbl.width = 4 + timed;
4568 tbl.flags = 0;
4569 tbl.flags = TABLE_NO_FREE;
4570 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4571 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4572 tbl.contents[0][0] = "Mask";
4573 tbl.contents[0][1] = "Set By";
4574 tbl.contents[0][2] = "Triggered";
4575 if(timed)
4576 {
4577 tbl.contents[0][3] = "Expires";
4578 tbl.contents[0][4] = "Reason";
4579 }
4580 else
4581 tbl.contents[0][3] = "Reason";
4582 if(!matches)
4583 {
4584 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
075d7932 4585/* reply("MSG_NONE"); */
d76ed9a9
AS
4586 free(tbl.contents[0]);
4587 free(tbl.contents);
4588 return 0;
4589 }
4590
4591 msg_never = user_find_message(user, "MSG_NEVER");
4592 for(ii = 0; ii < matches; )
4593 {
4594 ban = bans[ii];
4595
4596 if(!timed)
4597 expires = "";
4598 else if(ban->expires)
4599 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4600 else
4601 expires = msg_never;
4602
4603 if(ban->triggered)
4604 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4605 else
4606 triggered = msg_never;
4607
4608 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4609 tbl.contents[ii][0] = ban->mask;
4610 tbl.contents[ii][1] = ban->owner;
4611 tbl.contents[ii][2] = strdup(triggered);
4612 if(timed)
4613 {
4614 tbl.contents[ii][3] = strdup(expires);
4615 tbl.contents[ii][4] = ban->reason;
4616 }
4617 else
4618 tbl.contents[ii][3] = ban->reason;
4619 }
4620 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
de9510bc 4621 /* reply("MSG_MATCH_COUNT", matches); */
d76ed9a9
AS
4622 for(ii = 1; ii < tbl.length; ++ii)
4623 {
4624 free((char*)tbl.contents[ii][2]);
4625 if(timed)
4626 free((char*)tbl.contents[ii][3]);
4627 free(tbl.contents[ii]);
4628 }
4629 free(tbl.contents[0]);
4630 free(tbl.contents);
4631 return 1;
4632}
4633
b75e24a3
AS
4634/* bad_topic
4635 *
4636 * return + if the user does NOT have the right to set the topic, and
4637 * the topic is changed.
4638 */
d76ed9a9
AS
4639static int
4640bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4641{
4642 struct chanData *cData = channel->channel_info;
4643 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4644 return 0;
d76ed9a9
AS
4645 else if(cData->topic)
4646 return irccasecmp(new_topic, cData->topic);
4647 else
4648 return 0;
4649}
4650
b75e24a3
AS
4651/* conform_topic
4652 *
4653 * Makes a givin topic fit into a givin topic mask and returns
4654 * the results.
4655 *
4656 * topic_mask - the mask to conform to
4657 * topic - the topic to make conform
4658 * new_topic - the pre-allocated char* to put the new topic into
4659 *
4660 * modifies: new_topic
4661 */
4662void
4663conform_topic(char* topic_mask, char* topic, char *new_topic)
4664{
4665 //char *topic_mask = cData->topic_mask;
4666 char tchar;
4667 int pos=0, starpos=-1, dpos=0, len;
4668
4669 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4670 {
4671 switch(tchar)
4672 {
4673 case '*':
4674 if(starpos != -1)
4675 {
4676 strcpy(new_topic, "");
4677 return;
4678 }
4679 len = strlen(topic);
4680 if((dpos + len) > TOPICLEN)
4681 len = TOPICLEN + 1 - dpos;
4682 memcpy(new_topic+dpos, topic, len);
4683 dpos += len;
4684 starpos = pos;
4685 break;
4686 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4687 default: new_topic[dpos++] = tchar; break;
4688 }
4689 }
4690 if((dpos > TOPICLEN) || tchar)
4691 {
4692 strcpy(new_topic, "");
4693 return;
4694 }
4695 new_topic[dpos] = 0;
4696 return;
4697}
4698
d76ed9a9
AS
4699static CHANSERV_FUNC(cmd_topic)
4700{
4701 struct chanData *cData;
4702 char *topic;
7fda2b52 4703 int p10 = 0;
4704
4705#ifdef WITH_PROTOCOL_P10
4706 p10 = 1;
4707#endif
d76ed9a9
AS
4708
4709 cData = channel->channel_info;
4710 if(argc < 2)
4711 {
b75e24a3
AS
4712 if(cData->topic)
4713 {
75ef8cdc 4714 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
7fda2b52 4715 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, cData->topic, 1);
b75e24a3 4716 reply("CSMSG_TOPIC_SET", cData->topic);
d76ed9a9 4717 return 1;
b75e24a3 4718 }
d76ed9a9
AS
4719
4720 reply("CSMSG_NO_TOPIC", channel->name);
4721 return 0;
4722 }
4723
4724 topic = unsplit_string(argv + 1, argc - 1, NULL);
4725 /* If they say "!topic *", use an empty topic. */
4726 if((topic[0] == '*') && (topic[1] == 0))
4727 topic[0] = 0;
b75e24a3 4728
d76ed9a9
AS
4729 if(bad_topic(channel, user, topic))
4730 {
b75e24a3
AS
4731 reply("CSMSG_TOPIC_LOCKED", channel->name);
4732 return 0;
4733 }
4734 else
4735 {
4736 /* If there is a topicmask set, and the new topic doesnt match, make it */
4737 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
d76ed9a9 4738 {
b75e24a3
AS
4739 char *topic_mask = cData->topic_mask;
4740 char new_topic[TOPICLEN+1];
d76ed9a9 4741
b75e24a3
AS
4742 /* make a new topic fitting mask */
4743 conform_topic(topic_mask, topic, new_topic);
4744 if(!*new_topic)
d76ed9a9 4745 {
b75e24a3 4746 /* Topic couldnt fit into mask, was too long */
d76ed9a9
AS
4747 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4748 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4749 return 0;
4750 }
7fda2b52 4751 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, new_topic, 1);
d76ed9a9 4752 }
b75e24a3 4753 else /* No mask set, just set the topic */
7fda2b52 4754 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, topic, 1);
d76ed9a9 4755 }
d76ed9a9
AS
4756
4757 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4758 {
4759 /* Grab the topic and save it as the default topic. */
4760 free(cData->topic);
4761 cData->topic = strdup(channel->topic);
4762 }
4763
4764 return 1;
4765}
4766
4767static CHANSERV_FUNC(cmd_mode)
4768{
2f61d1d7 4769 struct userData *uData;
d76ed9a9 4770 struct mod_chanmode *change;
2f61d1d7 4771 short base_oplevel;
d76ed9a9
AS
4772
4773 if(argc < 2)
4774 {
08895577 4775 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
4776 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4777 return 0;
4778 }
4779
d76ed9a9
AS
4780 change = &channel->channel_info->modes;
4781 if(change->modes_set || change->modes_clear) {
4782 modcmd_chanmode_announce(change);
4783 reply("CSMSG_DEFAULTED_MODES", channel->name);
4784 } else
4785 reply("CSMSG_NO_MODES", channel->name);
4786 return 1;
4787 }
4788
2f61d1d7 4789 uData = GetChannelUser(channel->channel_info, user->handle_info);
4790 if (!uData)
4791 base_oplevel = MAXOPLEVEL;
4792 else if (uData->access >= UL_OWNER)
4793 base_oplevel = 1;
4794 else
4795 base_oplevel = 1 + UL_OWNER - uData->access;
4796 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4797
d76ed9a9
AS
4798 if(!change)
4799 {
4800 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4801 return 0;
4802 }
4803
4804 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4805 && mode_lock_violated(&channel->channel_info->modes, change))
4806 {
4807 char modes[MAXLEN];
4808 mod_chanmode_format(&channel->channel_info->modes, modes);
4809 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4810 return 0;
4811 }
4812
4813 modcmd_chanmode_announce(change);
4814 mod_chanmode_free(change);
4815 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4816 return 1;
4817}
4818
4819static CHANSERV_FUNC(cmd_invite)
4820{
4821 struct userData *uData;
4822 struct userNode *invite;
4823
4824 uData = GetChannelUser(channel->channel_info, user->handle_info);
4825
4826 if(argc > 1)
4827 {
4828 if(!(invite = GetUserH(argv[1])))
4829 {
4830 reply("MSG_NICK_UNKNOWN", argv[1]);
4831 return 0;
4832 }
4833 }
4834 else
4835 invite = user;
4836
4837 if(GetUserMode(channel, invite))
4838 {
4839 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4840 return 0;
4841 }
4842
4843 if(user != invite)
4844 {
4845 if(argc > 2)
4846 {
4847 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4848 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4849 }
4850 else
4851 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4852 }
5f1600ab 4853
22d5d8cf 4854 if (invite->handle_info && invite->handle_info->ignores->used && (argc > 1)) {
5f1600ab 4855 unsigned int i;
4856 for (i=0; i < invite->handle_info->ignores->used; i++) {
4857 if (user_matches_glob(user, invite->handle_info->ignores->list[i], MATCH_USENICK)) {
4858 reply("CSMSG_CANNOT_INVITE", argv[1], channel->name);
4859 return 0;
4860 }
4861 }
4862 }
4863
d76ed9a9
AS
4864 irc_invite(chanserv, invite, channel);
4865 if(argc > 1)
4866 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4867
4868 return 1;
4869}
4870
4871static CHANSERV_FUNC(cmd_inviteme)
4872{
4873 if(GetUserMode(channel, user))
4874 {
4875 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4876 return 0;
4877 }
4878 if(channel->channel_info
4879 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4880 {
4881 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4882 return 0;
4883 }
4884 irc_invite(cmd->parent->bot, user, channel);
4885 return 1;
4886}
4887
4888static void
4889show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4890{
4891 unsigned int combo;
4892 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4893
4894 /* We display things based on two dimensions:
4895 * - Issue time: present or absent
4896 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4897 * (in order of precedence, so something both expired and revoked
4898 * only counts as revoked)
4899 */
4900 combo = (suspended->issued ? 4 : 0)
4901 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4902 switch(combo) {
4903 case 0: /* no issue time, indefinite expiration */
4904 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4905 break;
4906 case 1: /* no issue time, expires in future */
4907 intervalString(buf1, suspended->expires-now, user->handle_info);
4908 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4909 break;
4910 case 2: /* no issue time, expired */
4911 intervalString(buf1, now-suspended->expires, user->handle_info);
4912 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4913 break;
4914 case 3: /* no issue time, revoked */
4915 intervalString(buf1, now-suspended->revoked, user->handle_info);
4916 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4917 break;
4918 case 4: /* issue time set, indefinite expiration */
4919 intervalString(buf1, now-suspended->issued, user->handle_info);
4920 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4921 break;
4922 case 5: /* issue time set, expires in future */
4923 intervalString(buf1, now-suspended->issued, user->handle_info);
4924 intervalString(buf2, suspended->expires-now, user->handle_info);
4925 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4926 break;
4927 case 6: /* issue time set, expired */
4928 intervalString(buf1, now-suspended->issued, user->handle_info);
4929 intervalString(buf2, now-suspended->expires, user->handle_info);
4930 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4931 break;
4932 case 7: /* issue time set, revoked */
4933 intervalString(buf1, now-suspended->issued, user->handle_info);
4934 intervalString(buf2, now-suspended->revoked, user->handle_info);
4935 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4936 break;
4937 default:
4938 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4939 return;
4940 }
4941}
4942
82f37c08 4943static void
4944show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4945{
4946 char buf[MAXLEN];
4947 const char *fmt = "%a %b %d %H:%M %Y";
4948 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4949
4950 if(giveownership->staff_issuer)
4951 {
4952 if(giveownership->reason)
4953 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4954 giveownership->target, giveownership->target_access,
4955 giveownership->staff_issuer, buf, giveownership->reason);
4956 else
4957 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4958 giveownership->target, giveownership->target_access,
4959 giveownership->staff_issuer, buf);
4960 }
4961 else
4962 {
4963 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4964 }
4965}
4966
4967
d76ed9a9
AS
4968static CHANSERV_FUNC(cmd_info)
4969{
4970 char modes[MAXLEN], buffer[INTERVALLEN];
4971 struct userData *uData, *owner;
4972 struct chanData *cData;
4973 struct do_not_register *dnr;
4974 struct note *note;
4975 dict_iterator_t it;
4976 int privileged;
4977
4978 cData = channel->channel_info;
4979 reply("CSMSG_CHANNEL_INFO", channel->name);
cde20a3f
AS
4980 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4981 reply("CSMSG_BAR");
d76ed9a9
AS
4982
4983 uData = GetChannelUser(cData, user->handle_info);
b75e24a3 4984 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9
AS
4985 {
4986 mod_chanmode_format(&cData->modes, modes);
4987 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4988 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4989 }
4990
4991 for(it = dict_first(cData->notes); it; it = iter_next(it))
4992 {
4993 int padding;
4994
4995 note = iter_data(it);
4996 if(!note_type_visible_to_user(cData, note->type, user))
4997 continue;
4998
4999 padding = PADLEN - 1 - strlen(iter_key(it));
5000 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
5001 }
5002
5003 reply("CSMSG_CHANNEL_MAX", cData->max);
5004 for(owner = cData->users; owner; owner = owner->next)
5005 if(owner->access == UL_OWNER)
5006 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
5007 reply("CSMSG_CHANNEL_USERS", cData->userCount);
c8273589 5008 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
d76ed9a9 5009 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
d76ed9a9
AS
5010
5011 privileged = IsStaff(user);
20517d48 5012 /* if(privileged) */
697f4c9a 5013 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
20517d48 5014 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData->registrar)
d76ed9a9
AS
5015 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
5016
5017 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
5018 chanserv_show_dnrs(user, cmd, channel->name, NULL);
5019
5020 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
5021 {
5022 struct suspended *suspended;
5023 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
5024 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
5025 show_suspension_info(cmd, user, suspended);
5026 }
5027 else if(IsSuspended(cData))
5028 {
5029 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
5030 show_suspension_info(cmd, user, cData->suspended);
5031 }
82f37c08 5032 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
5033 {
5034 struct giveownership *giveownership;
5035 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
5036 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
5037 show_giveownership_info(cmd, user, giveownership);
5038 }
cde20a3f
AS
5039 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5040 reply("CSMSG_CHANNEL_END");
5041 else
5042 reply("CSMSG_CHANNEL_END_CLEAN");
d76ed9a9
AS
5043 return 1;
5044}
5045
5046static CHANSERV_FUNC(cmd_netinfo)
5047{
5048 extern time_t boot_time;
5049 extern unsigned long burst_length;
5050 char interval[INTERVALLEN];
5051
5052 reply("CSMSG_NETWORK_INFO");
5053 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
5054 reply("CSMSG_NETWORK_USERS", dict_size(clients));
5055 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
5056 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
c8273589 5057 reply("CSMSG_NETWORK_LAMERS", banCount);
d76ed9a9
AS
5058 reply("CSMSG_NETWORK_CHANUSERS", userCount);
5059 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
5060 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
5061 return 1;
5062}
5063
5064static void
5065send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
5066{
5067 struct helpfile_table table;
5068 unsigned int nn;
5069 struct userNode *user;
5070 char *nick;
5071
5072 table.length = 0;
5073 table.width = 1;
5074 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
5075 table.contents = alloca(list->used*sizeof(*table.contents));
5076 for(nn=0; nn<list->used; nn++)
5077 {
5078 user = list->list[nn];
5079 if(user->modes & skip_flags)
5080 continue;
5081 if(IsBot(user))
5082 continue;
5083 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
5084 if(IsAway(user))
5085 {
5086 nick = alloca(strlen(user->nick)+3);
5087 sprintf(nick, "(%s)", user->nick);
5088 }
5089 else
5090 nick = user->nick;
5091 table.contents[table.length][0] = nick;
5092 table.length++;
5093 }
5094 table_send(chanserv, to->nick, 0, NULL, table);
5095}
5096
5097static CHANSERV_FUNC(cmd_ircops)
5098{
5099 reply("CSMSG_STAFF_OPERS");
5100 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
5101 return 1;
5102}
5103
5104static CHANSERV_FUNC(cmd_helpers)
5105{
5106 reply("CSMSG_STAFF_HELPERS");
5107 send_staff_list(user, &curr_helpers, FLAGS_OPER);
5108 return 1;
5109}
5110
5111static CHANSERV_FUNC(cmd_staff)
5112{
5113 reply("CSMSG_NETWORK_STAFF");
5114 cmd_ircops(CSFUNC_ARGS);
5115 cmd_helpers(CSFUNC_ARGS);
5116 return 1;
5117}
5118
5119static CHANSERV_FUNC(cmd_peek)
5120{
5121 struct modeNode *mn;
5122 char modes[MODELEN];
5123 unsigned int n;
5124 struct helpfile_table table;
5125
5126 irc_make_chanmode(channel, modes);
5127
5128 reply("CSMSG_PEEK_INFO", channel->name);
cde20a3f
AS
5129 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5130 reply("CSMSG_BAR");
d76ed9a9
AS
5131 reply("CSMSG_PEEK_TOPIC", channel->topic);
5132 reply("CSMSG_PEEK_MODES", modes);
5133 reply("CSMSG_PEEK_USERS", channel->members.used);
5134
5135 table.length = 0;
5136 table.width = 1;
5137 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
5138 table.contents = alloca(channel->members.used*sizeof(*table.contents));
5139 for(n = 0; n < channel->members.used; n++)
5140 {
5141 mn = channel->members.list[n];
5142 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
5143 continue;
5144 table.contents[table.length] = alloca(sizeof(**table.contents));
5145 table.contents[table.length][0] = mn->user->nick;
5146 table.length++;
5147 }
5148 if(table.length)
5149 {
5150 reply("CSMSG_PEEK_OPS");
5151 table_send(chanserv, user->nick, 0, NULL, table);
5152 }
5153 else
5154 reply("CSMSG_PEEK_NO_OPS");
de9510bc 5155 reply("CSMSG_PEEK_END");
d76ed9a9
AS
5156 return 1;
5157}
5158
5159static MODCMD_FUNC(cmd_wipeinfo)
5160{
5161 struct handle_info *victim;
0f6fe38c 5162 struct userData *ud, *actor;
d76ed9a9
AS
5163
5164 REQUIRE_PARAMS(2);
5165 actor = GetChannelUser(channel->channel_info, user->handle_info);
5166 if(!(victim = modcmd_get_handle_info(user, argv[1])))
5167 return 0;
5168 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
5169 {
5170 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
5171 return 0;
5172 }
5173 if((ud->access >= actor->access) && (ud != actor))
5174 {
5175 reply("MSG_USER_OUTRANKED", victim->handle);
5176 return 0;
5177 }
5178 if(ud->info)
5179 free(ud->info);
5180 ud->info = NULL;
5181 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
0f6fe38c 5182 return 1;
d76ed9a9
AS
5183}
5184
7637f48f 5185static void
5186resync_channel(struct chanNode *channel)
d76ed9a9
AS
5187{
5188 struct mod_chanmode *changes;
5189 struct chanData *cData = channel->channel_info;
5190 unsigned int ii, used;
5191
e3b2f789
AS
5192 /* 6 = worst case -ovh+ovh on everyone */
5193 changes = mod_chanmode_alloc(channel->members.used * 6);
d76ed9a9
AS
5194 for(ii = used = 0; ii < channel->members.used; ++ii)
5195 {
5196 struct modeNode *mn = channel->members.list[ii];
5197 struct userData *uData;
5198
5199 if(IsService(mn->user))
5200 continue;
5201
e3b2f789 5202
d76ed9a9 5203 uData = GetChannelAccess(cData, mn->user->handle_info);
e3b2f789
AS
5204
5205 /* If the channel is in no-mode mode, de-mode EVERYONE */
5206 if(cData->chOpts[chAutomode] == 'n')
55342ce8 5207 {
e3b2f789
AS
5208 if(mn->modes)
5209 {
5210 changes->args[used].mode = MODE_REMOVE | mn->modes;
5211 changes->args[used++].u.member = mn;
5212 }
55342ce8 5213 }
e3b2f789 5214 else /* Give various userlevels their modes.. */
d76ed9a9 5215 {
e3b2f789 5216 if(uData && uData->access >= UL_OP )
d76ed9a9 5217 {
e3b2f789
AS
5218 if(!(mn->modes & MODE_CHANOP))
5219 {
5220 changes->args[used].mode = MODE_CHANOP;
5221 changes->args[used++].u.member = mn;
5222 }
d76ed9a9 5223 }
e3b2f789 5224 else if(uData && uData->access >= UL_HALFOP)
55342ce8 5225 {
e3b2f789
AS
5226 if(mn->modes & MODE_CHANOP)
5227 {
5228 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
5229 changes->args[used++].u.member = mn;
5230 }
5231 if(!(mn->modes & MODE_HALFOP))
5232 {
5233 changes->args[used].mode = MODE_HALFOP;
5234 changes->args[used++].u.member = mn;
5235 }
55342ce8 5236 }
e3b2f789 5237 else if(uData && uData->access >= UL_PEON )
d76ed9a9 5238 {
e3b2f789
AS
5239 if(mn->modes & MODE_CHANOP)
5240 {
5241 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
5242 changes->args[used++].u.member = mn;
5243 }
5244 if(mn->modes & MODE_HALFOP)
5245 {
5246 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
5247 changes->args[used++].u.member = mn;
5248 }
5249 /* Don't voice peons if were in mode m */
5250 if( cData->chOpts[chAutomode] == 'm')
5251 {
5252 if(mn->modes & MODE_VOICE)
5253 {
5254 changes->args[used].mode = MODE_REMOVE | MODE_VOICE;
5255 changes->args[used++].u.member = mn;
5256 }
5257 }
5258 /* otherwise, make user they do have voice */
5259 else if(!(mn->modes & MODE_VOICE))
5260 {
5261 changes->args[used].mode = MODE_VOICE;
5262 changes->args[used++].u.member = mn;
5263 }
d76ed9a9 5264 }
e3b2f789 5265 else /* They arnt on the userlist.. */
d76ed9a9 5266 {
e3b2f789
AS
5267 /* If we voice everyone, but they dont.. */
5268 if(cData->chOpts[chAutomode] == 'v')
5269 {
5270 /* Remove anything except v */
5271 if(mn->modes & ~MODE_VOICE)
5272 {
5273 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5274 changes->args[used++].u.member = mn;
5275 }
5276 /* Add v */
5277 if(!(mn->modes & MODE_VOICE))
5278 {
5279 changes->args[used].mode = MODE_VOICE;
5280 changes->args[used++].u.member = mn;
5281 }
5282 }
5283 /* If we hop everyone, but they dont.. */
5284 else if(cData->chOpts[chAutomode] == 'h')
5285 {
5286 /* Remove anything except h */
5287 if(mn->modes & ~MODE_HALFOP)
5288 {
5289 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP);
5290 changes->args[used++].u.member = mn;
5291 }
5292 /* Add h */
5293 if(!(mn->modes & MODE_HALFOP))
5294 {
5295 changes->args[used].mode = MODE_HALFOP;
5296 changes->args[used++].u.member = mn;
5297 }
5298 }
5299 /* If we op everyone, but they dont.. */
5300 else if(cData->chOpts[chAutomode] == 'o')
5301 {
5302 /* Remove anything except h */
5303 if(mn->modes & ~MODE_CHANOP)
5304 {
5305 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP);
5306 changes->args[used++].u.member = mn;
5307 }
5308 /* Add h */
5309 if(!(mn->modes & MODE_CHANOP))
5310 {
5311 changes->args[used].mode = MODE_CHANOP;
5312 changes->args[used++].u.member = mn;
5313 }
5314 }
5315 /* they have no excuse for having modes, de-everything them */
5316 else
5317 {
5318 if(mn->modes)
5319 {
5320 changes->args[used].mode = MODE_REMOVE | mn->modes;
5321 changes->args[used++].u.member = mn;
5322 }
5323 }
d76ed9a9
AS
5324 }
5325 }
5326 }
5327 changes->argc = used;
7637f48f 5328 mod_chanmode_announce(chanserv, channel, changes);
d76ed9a9 5329 mod_chanmode_free(changes);
7637f48f 5330}
5331
5332static CHANSERV_FUNC(cmd_resync)
5333{
5334 resync_channel(channel);
d76ed9a9
AS
5335 reply("CSMSG_RESYNCED_USERS", channel->name);
5336 return 1;
5337}
5338
5339static CHANSERV_FUNC(cmd_seen)
5340{
5341 struct userData *uData;
5342 struct handle_info *handle;
5343 char seen[INTERVALLEN];
5344
5345 REQUIRE_PARAMS(2);
5346
5347 if(!irccasecmp(argv[1], chanserv->nick))
5348 {
5349 reply("CSMSG_IS_CHANSERV");
5350 return 1;
5351 }
5352
5353 if(!(handle = get_handle_info(argv[1])))
5354 {
5355 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5356 return 0;
5357 }
5358
5359 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5360 {
5361 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5362 return 0;
5363 }
5364
5365 if(uData->present)
5366 reply("CSMSG_USER_PRESENT", handle->handle);
5367 else if(uData->seen)
5368 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5369 else
5370 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5371
5372 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5373 reply("CSMSG_USER_VACATION", handle->handle);
5374
5375 return 1;
5376}
5377
5378static MODCMD_FUNC(cmd_names)
5379{
5380 struct userNode *targ;
5381 struct userData *targData;
5382 unsigned int ii, pos;
5383 char buf[400];
5384
5385 for(ii=pos=0; ii<channel->members.used; ++ii)
5386 {
5387 targ = channel->members.list[ii]->user;
5388 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5389 if(!targData)
5390 continue;
5391 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5392 {
5393 buf[pos] = 0;
5394 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5395 pos = 0;
5396 }
5397 buf[pos++] = ' ';
5398 if(IsUserSuspended(targData))
5399 buf[pos++] = 's';
5400 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5401 }
5402 buf[pos] = 0;
5403 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5404 reply("CSMSG_END_NAMES", channel->name);
5405 return 1;
5406}
5407
5408static int
5409note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5410{
5411 switch(ntype->visible_type)
5412 {
5413 case NOTE_VIS_ALL: return 1;
5414 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5415 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5416 }
5417}
5418
5419static int
5420note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5421{
5422 struct userData *uData;
5423
5424 switch(ntype->set_access_type)
5425 {
338a82b5
AS
5426 case NOTE_SET_CHANNEL_ACCESS:
5427 if(!user->handle_info)
5428 return 0;
5429 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5430 return 0;
5431 return uData->access >= ntype->set_access.min_ulevel;
5432 case NOTE_SET_CHANNEL_SETTER:
5433 return check_user_level(channel, user, lvlSetters, 1, 0);
5434 case NOTE_SET_PRIVILEGED: default:
5435 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
d76ed9a9
AS
5436 }
5437}
5438
5439static CHANSERV_FUNC(cmd_note)
5440{
5441 struct chanData *cData;
5442 struct note *note;
5443 struct note_type *ntype;
5444
5445 cData = channel->channel_info;
5446 if(!cData)
5447 {
5448 reply("CSMSG_NOT_REGISTERED", channel->name);
5449 return 0;
5450 }
5451
5452 /* If no arguments, show all visible notes for the channel. */
5453 if(argc < 2)
5454 {
5455 dict_iterator_t it;
5456 unsigned int count;
5457
5458 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5459 {
5460 note = iter_data(it);
5461 if(!note_type_visible_to_user(cData, note->type, user))
5462 continue;
5463 if(!count++)
5464 reply("CSMSG_NOTELIST_HEADER", channel->name);
5465 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5466 }
5467 if(count)
5468 reply("CSMSG_NOTELIST_END", channel->name);
5469 else
5470 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5471 }
5472 /* If one argument, show the named note. */
5473 else if(argc == 2)
5474 {
5475 if((note = dict_find(cData->notes, argv[1], NULL))
5476 && note_type_visible_to_user(cData, note->type, user))
5477 {
5478 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5479 }
5480 else if((ntype = dict_find(note_types, argv[1], NULL))
5481 && note_type_visible_to_user(NULL, ntype, user))
5482 {
5483 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5484 return 0;
5485 }
5486 else
5487 {
5488 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5489 return 0;
5490 }
5491 }
5492 /* Assume they're trying to set a note. */
5493 else
5494 {
5495 char *note_text;
5496 ntype = dict_find(note_types, argv[1], NULL);
5497 if(!ntype)
5498 {
5499 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5500 return 0;
5501 }
5502 else if(note_type_settable_by_user(channel, ntype, user))
5503 {
5504 note_text = unsplit_string(argv+2, argc-2, NULL);
5505 if((note = dict_find(cData->notes, argv[1], NULL)))
5506 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5507 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5508 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5509
5510 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5511 {
5512 /* The note is viewable to staff only, so return 0
5513 to keep the invocation from getting logged (or
5514 regular users can see it in !events). */
5515 return 0;
5516 }
5517 }
5518 else
5519 {
5520 reply("CSMSG_NO_ACCESS");
5521 return 0;
5522 }
5523 }
5524 return 1;
5525}
5526
5527static CHANSERV_FUNC(cmd_delnote)
5528{
5529 struct note *note;
5530
5531 REQUIRE_PARAMS(2);
5532 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5533 || !note_type_settable_by_user(channel, note->type, user))
5534 {
5535 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5536 return 0;
5537 }
5538 dict_remove(channel->channel_info->notes, note->type->name);
5539 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5540 return 1;
5541}
5542
23475fc6 5543static CHANSERV_FUNC(cmd_last)
5544{
5545 int numoflines;
5546
5547 REQUIRE_PARAMS(1);
5548
5549 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
5550
5551 if(numoflines < 1 || numoflines > 200)
5552 {
5553 reply("CSMSG_LAST_INVALID");
5554 return 0;
5555 }
5556 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
5557 return 1;
5558}
5559
d76ed9a9
AS
5560static CHANSERV_FUNC(cmd_events)
5561{
5562 struct logSearch discrim;
5563 struct logReport report;
5564 unsigned int matches, limit;
5565
5566 limit = (argc > 1) ? atoi(argv[1]) : 10;
5567 if(limit < 1 || limit > 200)
5568 limit = 10;
5569
5570 memset(&discrim, 0, sizeof(discrim));
5571 discrim.masks.bot = chanserv;
5572 discrim.masks.channel_name = channel->name;
5573 if(argc > 2)
5574 discrim.masks.command = argv[2];
5575 discrim.limit = limit;
5576 discrim.max_time = INT_MAX;
5577 discrim.severities = 1 << LOG_COMMAND;
5578 report.reporter = chanserv;
5579 report.user = user;
de9510bc 5580 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
cde20a3f
AS
5581 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5582 reply("CSMSG_BAR");
d76ed9a9
AS
5583 matches = log_entry_search(&discrim, log_report_entry, &report);
5584 if(matches)
5585 reply("MSG_MATCH_COUNT", matches);
5586 else
5587 reply("MSG_NO_MATCHES");
5588 return 1;
5589}
5590
5591static CHANSERV_FUNC(cmd_say)
5592{
5593 char *msg;
5594 if(channel)
5595 {
5596 REQUIRE_PARAMS(2);
5597 msg = unsplit_string(argv + 1, argc - 1, NULL);
5598 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5599 }
5600 else if(GetUserH(argv[1]))
5601 {
5602 REQUIRE_PARAMS(3);
5603 msg = unsplit_string(argv + 2, argc - 2, NULL);
5604 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5605 }
5606 else
5607 {
5608 reply("MSG_NOT_TARGET_NAME");
5609 return 0;
5610 }
5611 return 1;
5612}
5613
5614static CHANSERV_FUNC(cmd_emote)
5615{
5616 char *msg;
5617 assert(argc >= 2);
5618 if(channel)
5619 {
5620 /* CTCP is so annoying. */
5621 msg = unsplit_string(argv + 1, argc - 1, NULL);
5622 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5623 }
5624 else if(GetUserH(argv[1]))
5625 {
5626 msg = unsplit_string(argv + 2, argc - 2, NULL);
5627 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5628 }
5629 else
5630 {
5631 reply("MSG_NOT_TARGET_NAME");
5632 return 0;
5633 }
5634 return 1;
5635}
5636
5637struct channelList *
5638chanserv_support_channels(void)
5639{
5640 return &chanserv_conf.support_channels;
5641}
5642
5643static CHANSERV_FUNC(cmd_expire)
5644{
5645 int channel_count = registered_channels;
5646 expire_channels(NULL);
5647 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5648 return 1;
5649}
5650
5651static void
5652chanserv_expire_suspension(void *data)
5653{
5654 struct suspended *suspended = data;
5655 struct chanNode *channel;
d76ed9a9
AS
5656
5657 if(!suspended->expires || (now < suspended->expires))
5658 suspended->revoked = now;
5659 channel = suspended->cData->channel;
5660 suspended->cData->channel = channel;
5661 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
0d16e639 5662 if(!IsOffChannel(suspended->cData))
5663 {
63c95a47 5664 spamserv_cs_suspend(channel, 0, 0, NULL);
5665 ss_cs_join_channel(channel, 1);
0d16e639 5666 }
d76ed9a9
AS
5667}
5668
5669static CHANSERV_FUNC(cmd_csuspend)
5670{
5671 struct suspended *suspended;
5672 char reason[MAXLEN];
5673 time_t expiry, duration;
5674 struct userData *uData;
5675
5676 REQUIRE_PARAMS(3);
5677
5678 if(IsProtected(channel->channel_info))
5679 {
5680 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5681 return 0;
5682 }
5683
5684 if(argv[1][0] == '!')
5685 argv[1]++;
5686 else if(IsSuspended(channel->channel_info))
5687 {
5688 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5689 show_suspension_info(cmd, user, channel->channel_info->suspended);
5690 return 0;
5691 }
5692
5693 if(!strcmp(argv[1], "0"))
5694 expiry = 0;
5695 else if((duration = ParseInterval(argv[1])))
5696 expiry = now + duration;
5697 else
5698 {
5699 reply("MSG_INVALID_DURATION", argv[1]);
5700 return 0;
5701 }
5702
5703 unsplit_string(argv + 2, argc - 2, reason);
5704
5705 suspended = calloc(1, sizeof(*suspended));
5706 suspended->revoked = 0;
5707 suspended->issued = now;
5708 suspended->suspender = strdup(user->handle_info->handle);
5709 suspended->expires = expiry;
5710 suspended->reason = strdup(reason);
5711 suspended->cData = channel->channel_info;
5712 suspended->previous = suspended->cData->suspended;
5713 suspended->cData->suspended = suspended;
5714
5715 if(suspended->expires)
5716 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5717
5718 if(IsSuspended(channel->channel_info))
5719 {
5720 suspended->previous->revoked = now;
5721 if(suspended->previous->expires)
5722 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
09a3057c 5723
5724 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENSION_MODIFIED",
5725 channel->name, suspended->suspender);
d76ed9a9
AS
5726 }
5727 else
5728 {
5729 /* Mark all users in channel as absent. */
5730 for(uData = channel->channel_info->users; uData; uData = uData->next)
5731 {
5732 if(uData->present)
5733 {
5734 uData->seen = now;
5735 uData->present = 0;
5736 }
5737 }
5738
5739 /* Mark the channel as suspended, then part. */
5740 channel->channel_info->flags |= CHANNEL_SUSPENDED;
63c95a47 5741 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
d76ed9a9
AS
5742 DelChannelUser(chanserv, channel, suspended->reason, 0);
5743 reply("CSMSG_SUSPENDED", channel->name);
09a3057c 5744 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENDED_BY",
5745 channel->name, suspended->suspender);
d76ed9a9
AS
5746 }
5747 return 1;
5748}
5749
5750static CHANSERV_FUNC(cmd_cunsuspend)
5751{
5752 struct suspended *suspended;
d76ed9a9
AS
5753
5754 if(!IsSuspended(channel->channel_info))
5755 {
5756 reply("CSMSG_NOT_SUSPENDED", channel->name);
5757 return 0;
5758 }
5759
5760 suspended = channel->channel_info->suspended;
5761
5762 /* Expire the suspension and join ChanServ to the channel. */
5763 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5764 chanserv_expire_suspension(suspended);
5765 reply("CSMSG_UNSUSPENDED", channel->name);
09a3057c 5766 global_message_args(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, "CSMSG_UNSUSPENDED_BY",
5767 channel->name, user->handle_info->handle);
d76ed9a9
AS
5768 return 1;
5769}
5770
5771typedef struct chanservSearch
5772{
5773 char *name;
5774 char *registrar;
5775
5776 time_t unvisited;
5777 time_t registered;
5778
5779 unsigned long flags;
5780 unsigned int limit;
5781} *search_t;
5782
5783typedef void (*channel_search_func)(struct chanData *channel, void *data);
5784
5785static search_t
c092fcad 5786chanserv_search_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
d76ed9a9
AS
5787{
5788 search_t search;
5789 unsigned int i;
5790
5791 search = malloc(sizeof(struct chanservSearch));
5792 memset(search, 0, sizeof(*search));
5793 search->limit = 25;
5794
5795 for(i = 0; i < argc; i++)
5796 {
5797 /* Assume all criteria require arguments. */
5798 if(i == (argc - 1))
5799 {
c092fcad 5800 reply("MSG_MISSING_PARAMS", argv[i]);
d76ed9a9
AS
5801 goto fail;
5802 }
5803
5804 if(!irccasecmp(argv[i], "name"))
5805 search->name = argv[++i];
5806 else if(!irccasecmp(argv[i], "registrar"))
5807 search->registrar = argv[++i];
5808 else if(!irccasecmp(argv[i], "unvisited"))
5809 search->unvisited = ParseInterval(argv[++i]);
5810 else if(!irccasecmp(argv[i], "registered"))
5811 search->registered = ParseInterval(argv[++i]);
5812 else if(!irccasecmp(argv[i], "flags"))
5813 {
5814 i++;
5815 if(!irccasecmp(argv[i], "nodelete"))
5816 search->flags |= CHANNEL_NODELETE;
5817 else if(!irccasecmp(argv[i], "suspended"))
5818 search->flags |= CHANNEL_SUSPENDED;
5819 else
5820 {
c092fcad 5821 reply("CSMSG_INVALID_CFLAG", argv[i]);
d76ed9a9
AS
5822 goto fail;
5823 }
5824 }
5825 else if(!irccasecmp(argv[i], "limit"))
5826 search->limit = strtoul(argv[++i], NULL, 10);
5827 else
5828 {
c092fcad 5829 reply("MSG_INVALID_CRITERIA", argv[i]);
d76ed9a9
AS
5830 goto fail;
5831 }
5832 }
5833
5834 if(search->name && !strcmp(search->name, "*"))
5835 search->name = 0;
5836 if(search->registrar && !strcmp(search->registrar, "*"))
5837 search->registrar = 0;
5838
5839 return search;
5840 fail:
5841 free(search);
5842 return NULL;
5843}
5844
5845static int
5846chanserv_channel_match(struct chanData *channel, search_t search)
5847{
5848 const char *name = channel->channel->name;
5849 if((search->name && !match_ircglob(name, search->name)) ||
5850 (search->registrar && !channel->registrar) ||
5851 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5852 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5853 (search->registered && (now - channel->registered) > search->registered) ||
5854 (search->flags && ((search->flags & channel->flags) != search->flags)))
5855 return 0;
5856
5857 return 1;
5858}
5859
5860static unsigned int
5861chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5862{
5863 struct chanData *channel;
5864 unsigned int matches = 0;
5865
5866 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5867 {
5868 if(!chanserv_channel_match(channel, search))
5869 continue;
5870 matches++;
5871 smf(channel, data);
5872 }
5873
5874 return matches;
5875}
5876
5877static void
5878search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5879{
5880}
5881
5882static void
5883search_print(struct chanData *channel, void *data)
5884{
5885 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5886}
5887
5888static CHANSERV_FUNC(cmd_search)
5889{
5890 search_t search;
5891 unsigned int matches;
5892 channel_search_func action;
5893
5894 REQUIRE_PARAMS(3);
5895
5896 if(!irccasecmp(argv[1], "count"))
5897 action = search_count;
5898 else if(!irccasecmp(argv[1], "print"))
5899 action = search_print;
5900 else
5901 {
5902 reply("CSMSG_ACTION_INVALID", argv[1]);
5903 return 0;
5904 }
5905
c092fcad 5906 search = chanserv_search_create(cmd, user, argc - 2, argv + 2);
d76ed9a9
AS
5907 if(!search)
5908 return 0;
5909
5910 if(action == search_count)
5911 search->limit = INT_MAX;
5912
5913 if(action == search_print)
de9510bc 5914 {
cde20a3f
AS
5915 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5916 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5917 reply("CSMSG_BAR");
de9510bc 5918 }
d76ed9a9
AS
5919
5920 matches = chanserv_channel_search(search, action, user);
5921
5922 if(matches)
5923 reply("MSG_MATCH_COUNT", matches);
5924 else
5925 reply("MSG_NO_MATCHES");
5926
5927 free(search);
5928 return 1;
5929}
5930
5931static CHANSERV_FUNC(cmd_unvisited)
5932{
5933 struct chanData *cData;
5934 time_t interval = chanserv_conf.channel_expire_delay;
5935 char buffer[INTERVALLEN];
5936 unsigned int limit = 25, matches = 0;
5937
5938 if(argc > 1)
5939 {
5940 interval = ParseInterval(argv[1]);
5941 if(argc > 2)
5942 limit = atoi(argv[2]);
5943 }
5944
5945 intervalString(buffer, interval, user->handle_info);
5946 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5947
5948 for(cData = channelList; cData && matches < limit; cData = cData->next)
5949 {
5950 if((now - cData->visited) < interval)
5951 continue;
5952
5953 intervalString(buffer, now - cData->visited, user->handle_info);
5954 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5955 matches++;
5956 }
5957
5958 return 1;
5959}
5960
5961static MODCMD_FUNC(chan_opt_defaulttopic)
5962{
5963 if(argc > 1)
5964 {
5965 char *topic;
5966
5967 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5968 {
5969 reply("CSMSG_TOPIC_LOCKED", channel->name);
5970 return 0;
5971 }
5972
5973 topic = unsplit_string(argv+1, argc-1, NULL);
5974
5975 free(channel->channel_info->topic);
5976 if(topic[0] == '*' && topic[1] == 0)
5977 {
5978 topic = channel->channel_info->topic = NULL;
5979 }
5980 else
5981 {
5982 topic = channel->channel_info->topic = strdup(topic);
5983 if(channel->channel_info->topic_mask
5984 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5985 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5986 }
75ef8cdc 5987 SetChannelTopic(channel, chanserv, user, topic ? topic : "", 1);
d76ed9a9
AS
5988 }
5989
5990 if(channel->channel_info->topic)
5991 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5992 else
5993 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5994 return 1;
5995}
5996
5997static MODCMD_FUNC(chan_opt_topicmask)
5998{
5999 if(argc > 1)
6000 {
6001 struct chanData *cData = channel->channel_info;
6002 char *mask;
6003
6004 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
6005 {
6006 reply("CSMSG_TOPIC_LOCKED", channel->name);
6007 return 0;
6008 }
6009
6010 mask = unsplit_string(argv+1, argc-1, NULL);
6011
6012 if(cData->topic_mask)
6013 free(cData->topic_mask);
6014 if(mask[0] == '*' && mask[1] == 0)
6015 {
6016 cData->topic_mask = 0;
6017 }
6018 else
6019 {
6020 cData->topic_mask = strdup(mask);
6021 if(!cData->topic)
6022 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
6023 else if(!match_ircglob(cData->topic, cData->topic_mask))
6024 reply("CSMSG_TOPIC_MISMATCH", channel->name);
6025 }
6026 }
6027
6028 if(channel->channel_info->topic_mask)
6029 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
6030 else
6031 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
6032 return 1;
6033}
6034
6035int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
6036{
6037 if(argc > 1)
6038 {
6039 char *greeting = unsplit_string(argv+1, argc-1, NULL);
6040 char *previous;
6041
6042 previous = *data;
6043 if(greeting[0] == '*' && greeting[1] == 0)
6044 *data = NULL;
6045 else
6046 {
6047 unsigned int length = strlen(greeting);
6048 if(length > chanserv_conf.greeting_length)
6049 {
6050 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
6051 return 0;
6052 }
6053 *data = strdup(greeting);
6054 }
6055 if(previous)
6056 free(previous);
6057 }
6058
6059 if(*data)
6060 reply(name, *data);
6061 else
6062 reply(name, user_find_message(user, "MSG_NONE"));
6063 return 1;
6064}
6065
6066static MODCMD_FUNC(chan_opt_greeting)
6067{
6068 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
6069}
6070
6071static MODCMD_FUNC(chan_opt_usergreeting)
6072{
6073 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
6074}
6075
8b9e7d45 6076static MODCMD_FUNC(chan_opt_maxsetinfo)
6077{
6078 unsigned int charmax;
6079
6080 if(argc > 1) {
6081 charmax = atoi(argv[1]);
6082 if ((charmax > 0) && (charmax < chanserv_conf.max_userinfo_length))
6083 channel->channel_info->maxsetinfo = charmax;
6084 }
6085
6086 reply("CSMSG_SET_MAXSETINFO", channel->channel_info->maxsetinfo);
6087 return 1;
6088}
6089
d76ed9a9
AS
6090static MODCMD_FUNC(chan_opt_modes)
6091{
6092 struct mod_chanmode *new_modes;
6093 char modes[MODELEN];
6094
6095 if(argc > 1)
6096 {
08895577 6097 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
6098 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6099 return 0;
6100 }
6101
d76ed9a9
AS
6102 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
6103 {
6104 reply("CSMSG_NO_ACCESS");
6105 return 0;
6106 }
6107 if(argv[1][0] == '*' && argv[1][1] == 0)
6108 {
6109 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
6110 }
2f61d1d7 6111 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1,MCP_KEY_FREE|MCP_REGISTERED, 0)))
d76ed9a9
AS
6112 {
6113 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
6114 return 0;
6115 }
6116 else if(new_modes->argc > 1)
6117 {
6118 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
6119 mod_chanmode_free(new_modes);
6120 return 0;
6121 }
6122 else
6123 {
6124 channel->channel_info->modes = *new_modes;
6125 modcmd_chanmode_announce(new_modes);
6126 mod_chanmode_free(new_modes);
6127 }
6128 }
6129
6130 mod_chanmode_format(&channel->channel_info->modes, modes);
6131 if(modes[0])
6132 reply("CSMSG_SET_MODES", modes);
6133 else
6134 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
6135 return 1;
6136}
6137
6138#define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6139static int
6140channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6141{
6142 struct chanData *cData = channel->channel_info;
6143 int value;
6144
6145 if(argc > 1)
6146 {
6147 /* Set flag according to value. */
6148 if(enabled_string(argv[1]))
6149 {
6150 cData->flags |= mask;
6151 value = 1;
6152 }
6153 else if(disabled_string(argv[1]))
6154 {
6155 cData->flags &= ~mask;
6156 value = 0;
6157 }
6158 else
6159 {
6160 reply("MSG_INVALID_BINARY", argv[1]);
6161 return 0;
6162 }
6163 }
6164 else
6165 {
6166 /* Find current option value. */
6167 value = (cData->flags & mask) ? 1 : 0;
6168 }
6169
6170 if(value)
6171 reply(name, user_find_message(user, "MSG_ON"));
6172 else
6173 reply(name, user_find_message(user, "MSG_OFF"));
6174 return 1;
6175}
6176
6177static MODCMD_FUNC(chan_opt_nodelete)
6178{
6179 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
6180 {
6181 reply("MSG_SETTING_PRIVILEGED", argv[0]);
6182 return 0;
6183 }
6184
6185 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
6186}
6187
6188static MODCMD_FUNC(chan_opt_dynlimit)
6189{
6190 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
6191}
6192
6193static MODCMD_FUNC(chan_opt_offchannel)
6194{
6195 struct chanData *cData = channel->channel_info;
6196 int value;
6197
6198 if(argc > 1)
6199 {
6200 /* Set flag according to value. */
6201 if(enabled_string(argv[1]))
6202 {
6203 if(!IsOffChannel(cData))
6204 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
6205 cData->flags |= CHANNEL_OFFCHANNEL;
6206 value = 1;
6207 }
6208 else if(disabled_string(argv[1]))
6209 {
6210 if(IsOffChannel(cData))
6211 {
6212 struct mod_chanmode change;
6213 mod_chanmode_init(&change);
6214 change.argc = 1;
6215 change.args[0].mode = MODE_CHANOP;
a32da4c7 6216 change.args[0].u.member = AddChannelUser(chanserv, channel);
d76ed9a9
AS
6217 mod_chanmode_announce(chanserv, channel, &change);
6218 }
6219 cData->flags &= ~CHANNEL_OFFCHANNEL;
6220 value = 0;
6221 }
6222 else
6223 {
6224 reply("MSG_INVALID_BINARY", argv[1]);
6225 return 0;
6226 }
6227 }
6228 else
6229 {
6230 /* Find current option value. */
6231 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
6232 }
6233
6234 if(value)
6235 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
6236 else
6237 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
6238 return 1;
6239}
6240
6241static MODCMD_FUNC(chan_opt_defaults)
6242{
6243 struct userData *uData;
6244 struct chanData *cData;
6245 const char *confirm;
6246 enum levelOption lvlOpt;
6247 enum charOption chOpt;
6248
6249 cData = channel->channel_info;
6250 uData = GetChannelUser(cData, user->handle_info);
6251 if(!uData || (uData->access < UL_OWNER))
6252 {
6253 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6254 return 0;
6255 }
6256 confirm = make_confirmation_string(uData);
6257 if((argc < 2) || strcmp(argv[1], confirm))
6258 {
6259 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6260 return 0;
6261 }
0f6fe38c 6262 cData->flags = CHANNEL_DEFAULT_FLAGS;
d76ed9a9
AS
6263 cData->modes = chanserv_conf.default_modes;
6264 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6265 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6266 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6267 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6268 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6269 return 1;
6270}
6271
6272static int
6273channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6274{
6275 struct chanData *cData = channel->channel_info;
6276 struct userData *uData;
6277 unsigned short value;
6278
6279 if(argc > 1)
6280 {
6281 if(!check_user_level(channel, user, option, 1, 1))
6282 {
6283 reply("CSMSG_CANNOT_SET");
6284 return 0;
6285 }
6286 value = user_level_from_name(argv[1], UL_OWNER+1);
6287 if(!value && strcmp(argv[1], "0"))
6288 {
6289 reply("CSMSG_INVALID_ACCESS", argv[1]);
6290 return 0;
6291 }
6292 uData = GetChannelUser(cData, user->handle_info);
6293 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6294 {
6295 reply("CSMSG_BAD_SETLEVEL");
6296 return 0;
6297 }
6298 switch(option)
6299 {
4b6129c0
AS
6300 case lvlSetters:
6301 /* This test only applies to owners, since non-owners
6302 * trying to set an option to above their level get caught
6303 * by the CSMSG_BAD_SETLEVEL test above.
6304 */
6305 if(value > uData->access)
6306 {
6307 reply("CSMSG_BAD_SETTERS");
6308 return 0;
6309 }
6310 break;
6311 default:
6312 break;
d76ed9a9
AS
6313 }
6314 cData->lvlOpts[option] = value;
6315 }
6316 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6317 return argc > 1;
6318}
6319
6320static MODCMD_FUNC(chan_opt_enfops)
6321{
6322 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6323}
6324
55342ce8 6325static MODCMD_FUNC(chan_opt_enfhalfops)
6326{
6327 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
6328}
d76ed9a9
AS
6329static MODCMD_FUNC(chan_opt_enfmodes)
6330{
6331 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6332}
6333
6334static MODCMD_FUNC(chan_opt_enftopic)
6335{
6336 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6337}
6338
6339static MODCMD_FUNC(chan_opt_pubcmd)
6340{
6341 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6342}
6343
6344static MODCMD_FUNC(chan_opt_setters)
6345{
6346 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6347}
6348
d76ed9a9
AS
6349static MODCMD_FUNC(chan_opt_userinfo)
6350{
6351 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6352}
6353
d76ed9a9
AS
6354static MODCMD_FUNC(chan_opt_topicsnarf)
6355{
6356 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6357}
6358
6359static MODCMD_FUNC(chan_opt_inviteme)
6360{
6361 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6362}
6363
31f23f13
AS
6364/* TODO: Make look like this when no args are
6365 * given:
6366 * -X3- -------------------------------
6367 * -X3- BanTimeout: Bans are removed:
6368 * -X3- ----- * indicates current -----
6369 * -X3- 0: [*] Never.
6370 * -X3- 1: [ ] After 10 minutes.
6371 * -X3- 2: [ ] After 2 hours.
6372 * -X3- 3: [ ] After 4 hours.
6373 * -X3- 4: [ ] After 24 hours.
6374 * -X3- 5: [ ] After one week.
6375 * -X3- ------------- End -------------
6376 */
d76ed9a9
AS
6377static int
6378channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6379{
6380 struct chanData *cData = channel->channel_info;
6381 int count = charOptions[option].count, index;
6382
6383 if(argc > 1)
6384 {
6385 index = atoi(argv[1]);
6386
6387 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
6388 {
6389 reply("CSMSG_INVALID_NUMERIC", index);
6390 /* Show possible values. */
6391 for(index = 0; index < count; index++)
6392 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6393 return 0;
6394 }
6395
6396 cData->chOpts[option] = charOptions[option].values[index].value;
6397 }
6398 else
6399 {
6400 /* Find current option value. */
6401 find_value:
6402 for(index = 0;
6403 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
6404 index++);
6405 if(index == count)
6406 {
6407 /* Somehow, the option value is corrupt; reset it to the default. */
6408 cData->chOpts[option] = charOptions[option].default_value;
6409 goto find_value;
6410 }
6411 }
6412
6413 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6414 return 1;
6415}
6416
4b6129c0 6417static MODCMD_FUNC(chan_opt_automode)
c8ca69a0 6418{
4b6129c0 6419 return channel_multiple_option(chAutomode, CSFUNC_ARGS);
c8ca69a0
AS
6420}
6421
d76ed9a9
AS
6422static MODCMD_FUNC(chan_opt_protect)
6423{
6424 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6425}
6426
6427static MODCMD_FUNC(chan_opt_toys)
6428{
6429 return channel_multiple_option(chToys, CSFUNC_ARGS);
6430}
6431
6432static MODCMD_FUNC(chan_opt_ctcpreaction)
6433{
6434 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6435}
6436
31f23f13
AS
6437static MODCMD_FUNC(chan_opt_bantimeout)
6438{
6439 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
6440}
6441
d76ed9a9
AS
6442static MODCMD_FUNC(chan_opt_topicrefresh)
6443{
6444 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6445}
6446
7637f48f 6447static MODCMD_FUNC(chan_opt_resync)
6448{
6449 return channel_multiple_option(chResync, CSFUNC_ARGS);
6450}
6451
d76ed9a9
AS
6452static struct svccmd_list set_shows_list;
6453
6454static void
6455handle_svccmd_unbind(struct svccmd *target) {
6456 unsigned int ii;
6457 for(ii=0; ii<set_shows_list.used; ++ii)
6458 if(target == set_shows_list.list[ii])
6459 set_shows_list.used = 0;
6460}
6461
6462static CHANSERV_FUNC(cmd_set)
6463{
6464 struct svccmd *subcmd;
6465 char buf[MAXLEN];
6466 unsigned int ii;
6467
6468 /* Check if we need to (re-)initialize set_shows_list. */
6469 if(!set_shows_list.used)
6470 {
6471 if(!set_shows_list.size)
6472 {
6473 set_shows_list.size = chanserv_conf.set_shows->used;
6474 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6475 }
6476 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6477 {
6478 const char *name = chanserv_conf.set_shows->list[ii];
6479 sprintf(buf, "%s %s", argv[0], name);
6480 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6481 if(!subcmd)
6482 {
6483 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6484 continue;
6485 }
6486 svccmd_list_append(&set_shows_list, subcmd);
6487 }
6488 }
6489
6490 if(argc < 2)
6491 {
cde20a3f
AS
6492 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
6493 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6494 reply("CSMSG_BAR");
d76ed9a9
AS
6495 for(ii = 0; ii < set_shows_list.used; ii++)
6496 {
6497 subcmd = set_shows_list.list[ii];
6498 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6499 }
de9510bc 6500 reply("CSMSG_CHANNEL_OPTIONS_END");
d76ed9a9
AS
6501 return 1;
6502 }
6503
6504 sprintf(buf, "%s %s", argv[0], argv[1]);
6505 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6506 if(!subcmd)
6507 {
6508 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6509 return 0;
6510 }
6511 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6512 {
6513 reply("CSMSG_NO_ACCESS");
6514 return 0;
6515 }
6516
6517 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6518}
6519
6520static int
6521user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6522{
6523 struct userData *uData;
6524
6525 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6526 if(!uData)
6527 {
6528 reply("CSMSG_NOT_USER", channel->name);
6529 return 0;
6530 }
6531
6532 if(argc < 2)
6533 {
6534 /* Just show current option value. */
6535 }
6536 else if(enabled_string(argv[1]))
6537 {
6538 uData->flags |= mask;
6539 }
6540 else if(disabled_string(argv[1]))
6541 {
6542 uData->flags &= ~mask;
6543 }
6544 else
6545 {
6546 reply("MSG_INVALID_BINARY", argv[1]);
6547 return 0;
6548 }
6549
6550 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6551 return 1;
6552}
6553
c8ca69a0 6554static MODCMD_FUNC(user_opt_autoop)
d76ed9a9
AS
6555{
6556 struct userData *uData;
6557
6558 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6559 if(!uData)
6560 {
6561 reply("CSMSG_NOT_USER", channel->name);
6562 return 0;
6563 }
90e75ffd 6564 if(uData->access < UL_HALFOP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
c8ca69a0 6565 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
d76ed9a9 6566 else
c8ca69a0 6567 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
d76ed9a9
AS
6568}
6569
6570static MODCMD_FUNC(user_opt_autoinvite)
6571{
6572 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6573}
6574
cd25f2e9 6575static MODCMD_FUNC(user_opt_autojoin)
6576{
6577 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN, CSFUNC_ARGS);
6578}
6579
d76ed9a9
AS
6580static MODCMD_FUNC(user_opt_info)
6581{
6582 struct userData *uData;
6583 char *infoline;
6584
6585 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6586
6587 if(!uData)
6588 {
6589 /* If they got past the command restrictions (which require access)
6590 * but fail this test, we have some fool with security override on.
6591 */
6592 reply("CSMSG_NOT_USER", channel->name);
6593 return 0;
6594 }
6595
6596 if(argc > 1)
6597 {
6598 size_t bp;
6599 infoline = unsplit_string(argv + 1, argc - 1, NULL);
8b9e7d45 6600 if(strlen(infoline) > channel->channel_info->maxsetinfo)
d76ed9a9 6601 {
8b9e7d45 6602 reply("CSMSG_INFOLINE_TOO_LONG", channel->channel_info->maxsetinfo);
d76ed9a9
AS
6603 return 0;
6604 }
6605 bp = strcspn(infoline, "\001");
6606 if(infoline[bp])
6607 {
6608 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6609 return 0;
6610 }
6611 if(uData->info)
6612 free(uData->info);
6613 if(infoline[0] == '*' && infoline[1] == 0)
6614 uData->info = NULL;
6615 else
6616 uData->info = strdup(infoline);
6617 }
6618 if(uData->info)
6619 reply("CSMSG_USET_INFO", uData->info);
6620 else
6621 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6622 return 1;
6623}
6624
6625struct svccmd_list uset_shows_list;
6626
6627static CHANSERV_FUNC(cmd_uset)
6628{
6629 struct svccmd *subcmd;
6630 char buf[MAXLEN];
6631 unsigned int ii;
6632
6633 /* Check if we need to (re-)initialize uset_shows_list. */
6634 if(!uset_shows_list.used)
6635 {
6636 char *options[] =
6637 {
cd25f2e9 6638 "AutoOp", "AutoInvite", "AutoJoin", "Info"
d76ed9a9
AS
6639 };
6640
6641 if(!uset_shows_list.size)
6642 {
6643 uset_shows_list.size = ArrayLength(options);
6644 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6645 }
6646 for(ii = 0; ii < ArrayLength(options); ii++)
6647 {
6648 const char *name = options[ii];
6649 sprintf(buf, "%s %s", argv[0], name);
6650 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6651 if(!subcmd)
6652 {
6653 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6654 continue;
6655 }
6656 svccmd_list_append(&uset_shows_list, subcmd);
6657 }
6658 }
6659
6660 if(argc < 2)
6661 {
6662 /* Do this so options are presented in a consistent order. */
6663 reply("CSMSG_USER_OPTIONS");
6664 for(ii = 0; ii < uset_shows_list.used; ii++)
6665 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6666 return 1;
6667 }
6668
6669 sprintf(buf, "%s %s", argv[0], argv[1]);
6670 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6671 if(!subcmd)
6672 {
6673 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6674 return 0;
6675 }
6676
6677 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6678}
6679
6680static CHANSERV_FUNC(cmd_giveownership)
6681{
6682 struct handle_info *new_owner_hi;
0f6fe38c 6683 struct userData *new_owner, *curr_user;
d76ed9a9
AS
6684 struct chanData *cData = channel->channel_info;
6685 struct do_not_register *dnr;
82f37c08 6686 struct giveownership *giveownership;
6687 unsigned int force, override;
6688 unsigned short co_access, new_owner_old_access;
09a3057c 6689 char transfer_reason[MAXLEN];
d76ed9a9
AS
6690
6691 REQUIRE_PARAMS(2);
6692 curr_user = GetChannelAccess(cData, user->handle_info);
6693 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
82f37c08 6694
6695 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6696 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6697 && (uData->access > 500)
6698 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6699 || uData->access < 500));
6700
6701
d76ed9a9
AS
6702 if(!curr_user || (curr_user->access != UL_OWNER))
6703 {
6704 struct userData *owner = NULL;
6705 for(curr_user = channel->channel_info->users;
6706 curr_user;
6707 curr_user = curr_user->next)
6708 {
6709 if(curr_user->access != UL_OWNER)
6710 continue;
6711 if(owner)
6712 {
6713 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6714 return 0;
6715 }
6716 owner = curr_user;
6717 }
6718 curr_user = owner;
6719 }
0f6fe38c 6720 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
a32da4c7 6721 {
6722 char delay[INTERVALLEN];
6723 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6724 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6725 return 0;
6726 }
5678501c 6727 if (!curr_user) {
6728 reply("CSMSG_NO_OWNER", channel->name);
6729 return 0;
6730 }
d76ed9a9
AS
6731 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6732 return 0;
6733 if(new_owner_hi == user->handle_info)
6734 {
6735 reply("CSMSG_NO_TRANSFER_SELF");
6736 return 0;
6737 }
6738 new_owner = GetChannelAccess(cData, new_owner_hi);
6739 if(!new_owner)
6740 {
0d16e639 6741 if(force)
6742 {
0f6fe38c 6743 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL, 0);
0d16e639 6744 }
6745 else
6746 {
6747 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6748 return 0;
6749 }
d76ed9a9
AS
6750 }
6751 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6752 {
6753 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6754 return 0;
6755 }
6756 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6757 if(!IsHelping(user))
6758 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6759 else
1117fc5a 6760 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
d76ed9a9
AS
6761 return 0;
6762 }
82f37c08 6763
6764 new_owner_old_access = new_owner->access;
d76ed9a9
AS
6765 if(new_owner->access >= UL_COOWNER)
6766 co_access = new_owner->access;
6767 else
6768 co_access = UL_COOWNER;
6769 new_owner->access = UL_OWNER;
6770 if(curr_user)
6771 curr_user->access = co_access;
a32da4c7 6772 cData->ownerTransfer = now;
82f37c08 6773
6774 giveownership = calloc(1, sizeof(*giveownership));
6775 giveownership->issued = now;
8ce9df05
AS
6776 giveownership->old_owner = strdup(curr_user->handle->handle);
6777 giveownership->target = strdup(new_owner_hi->handle);
82f37c08 6778 giveownership->target_access = new_owner_old_access;
6779 if(override)
6780 {
6781 if(argc > (2 + force))
6782 {
6783 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6784 giveownership->reason = strdup(transfer_reason);
6785 }
6786 giveownership->staff_issuer = strdup(user->handle_info->handle);
6787 }
6788
6789 giveownership->previous = channel->channel_info->giveownership;
6790 channel->channel_info->giveownership = giveownership;
6791
d76ed9a9 6792 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
09a3057c 6793 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_OWNERSHIP_TRANSFERRED",
6794 channel->name, new_owner_hi->handle, user->handle_info->handle);
d76ed9a9
AS
6795 return 1;
6796}
6797
b10abdb2 6798static void
6799chanserv_expire_user_suspension(void *data)
6800{
6801 struct userData *target = data;
6802
6803 target->expires = 0;
6804 target->flags &= ~USER_SUSPENDED;
6805}
6806
d76ed9a9
AS
6807static CHANSERV_FUNC(cmd_suspend)
6808{
6809 struct handle_info *hi;
0f6fe38c 6810 struct userData *self, *target;
b10abdb2 6811 time_t expiry;
d76ed9a9 6812
b10abdb2 6813 REQUIRE_PARAMS(3);
d76ed9a9
AS
6814 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6815 self = GetChannelUser(channel->channel_info, user->handle_info);
6816 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6817 {
6818 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6819 return 0;
6820 }
6821 if(target->access >= self->access)
6822 {
6823 reply("MSG_USER_OUTRANKED", hi->handle);
6824 return 0;
6825 }
6826 if(target->flags & USER_SUSPENDED)
6827 {
6828 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6829 return 0;
6830 }
6831 if(target->present)
6832 {
6833 target->present = 0;
6834 target->seen = now;
6835 }
b10abdb2 6836 if(!strcmp(argv[2], "0"))
6837 expiry = 0;
6838 else
6839 {
6840 unsigned int duration;
6841 if(!(duration = ParseInterval(argv[2])))
6842 {
6843 reply("MSG_INVALID_DURATION", argv[2]);
6844 return 0;
6845 }
6846 expiry = now + duration;
6847 }
6848
6849 target->expires = expiry;
6850
6851 if(target->expires)
6852 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6853
d76ed9a9
AS
6854 target->flags |= USER_SUSPENDED;
6855 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
0f6fe38c 6856 return 1;
d76ed9a9
AS
6857}
6858
6859static CHANSERV_FUNC(cmd_unsuspend)
6860{
6861 struct handle_info *hi;
0f6fe38c 6862 struct userData *self, *target;
d76ed9a9
AS
6863
6864 REQUIRE_PARAMS(2);
6865 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6866 self = GetChannelUser(channel->channel_info, user->handle_info);
6867 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6868 {
6869 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6870 return 0;
6871 }
6872 if(target->access >= self->access)
6873 {
6874 reply("MSG_USER_OUTRANKED", hi->handle);
6875 return 0;
6876 }
6877 if(!(target->flags & USER_SUSPENDED))
6878 {
6879 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6880 return 0;
6881 }
6882 target->flags &= ~USER_SUSPENDED;
a32da4c7 6883 scan_user_presence(target, NULL);
b10abdb2 6884 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
d76ed9a9 6885 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
0f6fe38c 6886 return 1;
d76ed9a9
AS
6887}
6888
6889static MODCMD_FUNC(cmd_deleteme)
6890{
6891 struct handle_info *hi;
6892 struct userData *target;
6893 const char *confirm_string;
6894 unsigned short access;
6895 char *channel_name;
6896
6897 hi = user->handle_info;
6898 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6899 {
6900 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6901 return 0;
6902 }
6903 if(target->access == UL_OWNER)
6904 {
6905 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6906 return 0;
6907 }
6908 confirm_string = make_confirmation_string(target);
6909 if((argc < 2) || strcmp(argv[1], confirm_string))
6910 {
6911 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6912 return 0;
6913 }
6914 access = target->access;
6915 channel_name = strdup(channel->name);
6916 del_channel_user(target, 1);
6917 reply("CSMSG_DELETED_YOU", access, channel_name);
6918 free(channel_name);
6919 return 1;
6920}
6921
6922static void
6923chanserv_refresh_topics(UNUSED_ARG(void *data))
6924{
6925 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6926 struct chanData *cData;
6927 char opt;
6928
6929 for(cData = channelList; cData; cData = cData->next)
6930 {
6931 if(IsSuspended(cData))
6932 continue;
6933 opt = cData->chOpts[chTopicRefresh];
6934 if(opt == 'n')
6935 continue;
6936 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6937 continue;
6938 if(cData->topic)
7fda2b52 6939 SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1);
d76ed9a9
AS
6940 cData->last_refresh = refresh_num;
6941 }
6942 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6943}
6944
7637f48f 6945static void
6946chanserv_auto_resync(UNUSED_ARG(void *data))
6947{
6948 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6949 struct chanData *cData;
6950 char opt;
6951
6952 for(cData = channelList; cData; cData = cData->next)
6953 {
6954 if(IsSuspended(cData)) continue;
6955 opt = cData->chOpts[chResync];
6956 if(opt == 'n') continue;
6957 if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
6958 resync_channel(cData->channel);
6959 cData->last_resync = refresh_num;
6960 }
6961 timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
6962}
6963
d76ed9a9
AS
6964static CHANSERV_FUNC(cmd_unf)
6965{
6966 if(channel)
6967 {
6968 char response[MAXLEN];
6969 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6970 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6971 irc_privmsg(cmd->parent->bot, channel->name, response);
6972 }
6973 else
6974 reply("CSMSG_UNF_RESPONSE");
6975 return 1;
6976}
6977
6978static CHANSERV_FUNC(cmd_ping)
6979{
6980 if(channel)
6981 {
6982 char response[MAXLEN];
6983 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6984 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6985 irc_privmsg(cmd->parent->bot, channel->name, response);
6986 }
6987 else
6988 reply("CSMSG_PING_RESPONSE");
6989 return 1;
6990}
6991
6992static CHANSERV_FUNC(cmd_wut)
6993{
6994 if(channel)
6995 {
6996 char response[MAXLEN];
6997 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6998 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6999 irc_privmsg(cmd->parent->bot, channel->name, response);
7000 }
7001 else
7002 reply("CSMSG_WUT_RESPONSE");
7003 return 1;
7004}
7005
87708af4 7006static CHANSERV_FUNC(cmd_roulette)
7007{
b404335b 7008 if(channel) {
7009 struct chanData *cData = channel->channel_info;
87708af4 7010
b404335b 7011 if (cData) {
7012 if (cData->roulette_chamber) {
fba880b8 7013 DelUser(user, chanserv, 1, "BANG - Don't stuff bullets into a loaded gun");
b404335b 7014 return 1;
7015 }
7016
7017 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_ROULETTE_LOADS");
7018 cData->roulette_chamber = 1 + rand() % 6;
7019 }
7020 }
7021
7022 return 1;
7023}
7024static CHANSERV_FUNC(cmd_shoot)
7025{
7026 if(channel) {
7027 struct chanData *cData = channel->channel_info;
7028
7029 if (cData->roulette_chamber <= 0) {
7030 reply("CSMSG_ROULETTE_NEW");
87708af4 7031 return 1;
7032 }
7033
b404335b 7034 cData->roulette_chamber--;
7035
7036 if (cData->roulette_chamber == 0) {
7037 reply("CSMSG_ROULETTE_BANG");
7038 reply("CSMSG_ROULETTE_BETTER_LUCK", user->nick);
fba880b8 7039 DelUser(user, chanserv, 1, "BANG!!!!");
b404335b 7040 } else
7041 reply("CSMSG_ROULETTE_CLICK");
87708af4 7042 }
b404335b 7043
87708af4 7044 return 1;
7045}
b404335b 7046
7047static void
7048chanserv_remove_abuse(void *data)
87708af4 7049{
c64a32cb
AS
7050 char *remnick = data;
7051 struct userNode *user;
7052 /* sometimes the clone was killed and maybe even the user took their nick back
7053 * (ie, an oper) so dont kill them here after all unless they are local. */
7054 if( (user = GetUserH(remnick)) )
7055 if(IsLocal(user) )
7056 DelUser(user, NULL, 1, "");
b404335b 7057}
87708af4 7058
e03ec3dc 7059int lamepart(struct userNode *nick) {
7060 struct modeNode *mn;
7061 unsigned int count, n;
7062
7063 for (n=count=0; n<nick->channels.used; n++) {
7064 mn = nick->channels.list[n];
7065 irc_svspart(chanserv, nick, mn->channel);
7066 }
7067
7068 return 0;
7069}
7070
b404335b 7071static CHANSERV_FUNC(cmd_spin)
7072{
7073 if(!channel)
87708af4 7074 return 1;
b404335b 7075
e03ec3dc 7076 int type, lamep = 1;
7077 char *tstr;
7078
7079 tstr = conf_get_data("server/type", RECDB_QSTRING);
7080 if(tstr) {
7081 type = atoi(tstr);
7082 if (type > 6)
7083 lamep = 0;
7084 }
7085
b404335b 7086 int wheel = 1 + rand() % 12;
7087
7088 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL1", user->nick);
7089 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL2");
7090 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL3");
7091
7092 if (wheel == 1) {
7093 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_1");
e91c6c1e 7094 if (type < 7)
7095 irc_kill(chanserv, user, "Connection reset by peer");
7096 else
7097 sputsock("%s SQ %s :Connection reset by peer", self->numeric, user->numeric);
b404335b 7098 }
7099 if (wheel == 2) {
7100 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_2");
e03ec3dc 7101 if (lamep)
7102 lamepart(user);
7103 else
7104 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
b404335b 7105 }
7106 if (wheel == 3) {
7107 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_3");
e03ec3dc 7108 if (lamep)
7109 lamepart(user);
7110 else
7111 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
87708af4 7112 }
b404335b 7113 if (wheel == 4) {
7114 char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
7115 int wtime = 120 + rand() % 600;
87708af4 7116
b404335b 7117 strcpy(target + 2, user->hostname);
7118 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_4");
87708af4 7119
b404335b 7120 gline_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1, 0);
7121 irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7122 }
7123 if (wheel == 5) {
7124 char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
7125 int wtime = 120 + rand() % 600;
87708af4 7126
b404335b 7127 strcpy(target + 2, user->hostname);
7128 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_5");
7129
7130 shun_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1);
7131 }
7132 if (wheel == 6) {
7133 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_6");
7134 }
7135 if (wheel == 7) {
7136 int complete = 0;
7137 int rndchans = 0;
7138 int chango = 0;
7139 int roundz0r = 0;
7140
7141 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_7");
7142 while(complete != 1) {
7143 if (rndchans != 15) {
7144 chango = 120 + rand() % 600;
7145 sputsock("%s SJ %s #%d %ld", self->numeric, user->numeric, chango, now);
7146 rndchans++;
7147 } else {
7148 if (roundz0r != 1) {
e03ec3dc 7149 if (lamep)
7150 lamepart(user);
7151 else
7152 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
b404335b 7153 roundz0r = 1;
7154 rndchans = 0;
7155 } else {
e03ec3dc 7156 if (lamep)
7157 lamepart(user);
7158 else
7159 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
7160 complete = 1;
b404335b 7161 }
7162 }
7163 }
7164 }
7165 if (wheel == 8) {
7166 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_8");
7167 irc_swhois(chanserv, user, "is being defecated on by services");
7168 }
7169 if (wheel == 9) {
7170 unsigned int count, n;
7171 struct modeNode *mn;
7172
7173 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_9");
7174
7175 for (n=count=0; n<user->channels.used; n++) {
7176 mn = user->channels.list[n];
7177 irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
7178 }
7179 }
7180 if (wheel == 10) {
7181 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_10");
7182
7183 char *oldnick = NULL;
7184 char *oldident = NULL;
7185 char *oldhost = NULL;
7186 char abusednick[NICKLEN] = "";
7187 int abusednum = time(NULL);
7188 struct userNode *clone;
7189
7190 oldnick = strdup(user->nick);
7191 oldident = strdup(user->ident);
7192 oldhost = strdup(user->hostname);
7193
7194 snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7195 while (1) {
7196 log_module(MAIN_LOG, LOG_DEBUG, "Abused Nick: %s, Client Nick: %s", abusednick, user->nick);
7197 snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7198 if (user->nick != abusednick)
7199 break;
7200 }
7201
cf8bedff 7202 SVSNickChange(user, abusednick);
b404335b 7203 irc_svsnick(chanserv, user, abusednick);
7204
7205 clone = AddClone(oldnick, oldident, oldhost, "I got abused by the wheel of misfortune :D");
c64a32cb 7206 timeq_add(now + 300, chanserv_remove_abuse, clone->nick);
b404335b 7207 }
7208 if (wheel == 11) {
7209 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_11");
7210
7211 irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7212 }
7213 if (wheel == 12) {
7214 int gagged, ignoretime = 0;
7215 char target[IRC_NTOP_MAX_SIZE + 13] = { '+', 'b', ' ', '*', '!', '*', '@', '\0' };
7216
7217 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_12");
7218
7219 strcpy(target + 4, user->hostname);
7220 srand(time(NULL));
7221 ignoretime = now + (1 + rand() % 120);
7222
7223 gagged = gag_create(target, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime);
7224 }
7225 if (wheel == 13) {
7226 unsigned int count, n;
7227 struct modeNode *mn;
7228 char target[IRC_NTOP_MAX_SIZE + 1];
7229
7230 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_13");
7231
7232 snprintf(target, sizeof(target), "+b *!*@%s", user->hostname);
7233 for (n=count=0; n<user->channels.used; n++) {
7234 mn = user->channels.list[n];
7235 irc_mode(chanserv, mn->channel, target);
7236 irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
7237 }
7238 }
7239
7240 return 1;
87708af4 7241}
7242
240a3274 7243#ifdef lame8ball
d76ed9a9
AS
7244static CHANSERV_FUNC(cmd_8ball)
7245{
7246 unsigned int i, j, accum;
7247 const char *resp;
7248
7249 REQUIRE_PARAMS(2);
7250 accum = 0;
7251 for(i=1; i<argc; i++)
7252 for(j=0; argv[i][j]; j++)
7253 accum = (accum << 5) - accum + toupper(argv[i][j]);
7254 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7255 if(channel)
7256 {
7257 char response[MAXLEN];
7258 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
7259 irc_privmsg(cmd->parent->bot, channel->name, response);
7260 }
7261 else
7262 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7263 return 1;
7264}
7265
240a3274
AS
7266#else /* Use cool 8ball instead */
7267
7268void eightball(char *outcome, int method, unsigned int seed)
7269{
7270 int answer = 0;
7271
7272#define NUMOFCOLORS 18
7273 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
7274 "white", "black", "grey", "brown",
7275 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7276 "fuchsia","turquoise","magenta", "cyan"};
7277#define NUMOFLOCATIONS 50
7278 char balllocations[50][55] = {
7279 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7280 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7281 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7282 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7283 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7284 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7285 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7286 "your bra", "your hair", "your bed", "the couch", "the wall",
7287 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7288 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7289#define NUMOFPREPS 15
7290 char ballpreps[50][50] = {
7291 "Near", "Somewhere near", "In", "In", "In",
7292 "In", "Hiding in", "Under", "Next to", "Over",
7293 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7294#define NUMOFNUMS 34
7295 char ballnums[50][50] = {
7296 "A hundred", "A thousand", "A few", "42",
7297 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7298 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7299 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7300 };
7301#define NUMOFMULTS 8
7302 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7303
7304 /* Method:
7305 * 0: normal (Not used in x3)
7306 * 1: color
7307 * 2: where is
7308 * 3: how many
7309 */
7310
7311 srand(seed);
7312 if (method == 1) /* A Color */
7313 {
7314 char tmp[MAXLEN];
7315
7316 answer = (rand() % 12); /* Make sure this is the # of entries */
7317 switch(answer)
7318 {
7319 case 0: strcpy(tmp, "Very bright %s, I'd say.");
7320 break;
7321 case 1: strcpy(tmp, "Sort of a light %s color.");
7322 break;
7323 case 2: strcpy(tmp, "Dark and dreary %s.");
7324 break;
7325 case 3: strcpy(tmp, "Quite a pale shade of %s.");
7326 break;
7327 case 4: strcpy(tmp, "A gross kind of mucky %s.");
7328 break;
7329 case 5: strcpy(tmp, "Brilliant whiteish %s.");
338a82b5
AS
7330 break;
7331 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
7332 break;
7333 case 10: strcpy(tmp, "Solid %s.");
7334 break;
7335 case 11: strcpy(tmp, "Transparent %s.");
7336 break;
240a3274
AS
7337 default: strcpy(outcome, "An invalid random number was generated.");
7338 return;
7339 }
7340 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
7341 return;
7342 }
7343 else if (method == 2) /* Location */
7344 {
7345 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
7346 }
7347 else if (method == 3) /* Number of ___ */
7348 {
7349 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
7350 }
7351 else
7352 {
7353 //Debug(DBGWARNING, "Error in 8ball.");
7354 }
7355 return;
7356}
7357
7358static CHANSERV_FUNC(cmd_8ball)
7359{
7360 char *word1, *word2, *word3;
7361 static char eb[MAXLEN];
7362 unsigned int accum, i, j;
7363
132f7859 7364 REQUIRE_PARAMS(2);
240a3274
AS
7365 accum = 0;
7366 for(i=1; i<argc; i++)
7367 for(j=0; argv[i][j]; j++)
7368 accum = (accum << 5) - accum + toupper(argv[i][j]);
7369
7370 accum += time(NULL)/3600;
7371 word1 = argv[1];
7372 word2 = argc>2?argv[2]:"";
7373 word3 = argc>3?argv[3]:"";
7374
7375/*** COLOR *****/
7376 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
7377 eightball(eb, 1, accum);
7378 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
7379 eightball(eb, 1, accum);
7380 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
7381 eightball(eb, 1, accum);
7382/*** LOCATION *****/
7383 else if(
7384 (
7385 word2 &&
7386 (
7387 (strcasecmp(word1, "where") == 0) &&
7388 (strcasecmp(word2, "is") == 0)
7389 )
7390 ) ||
7391 (
7392 strcasecmp(word1, "where's") == 0
7393 )
7394 )
7395 eightball(eb, 2, accum);
7396/*** NUMBER *****/
7397 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
7398 eightball(eb, 3, accum);
7399/*** GENERIC *****/
7400 else
7401 {
7402 /* Generic 8ball question.. so pull from x3.conf srvx style */
7403 const char *resp;
7404
7405 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7406 if(channel)
7407 {
7408 char response[MAXLEN];
7409 sprintf(response, "\002%s\002: %s", user->nick, resp);
7410 irc_privmsg(cmd->parent->bot, channel->name, response);
7411 }
7412 else
7413 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7414 return 1;
7415 }
7416
7417 if(channel)
7418 {
7419 char response[MAXLEN];
7420 sprintf(response, "\002%s\002: %s", user->nick, eb);
7421 irc_privmsg(cmd->parent->bot, channel->name, response);
7422 }
7423 else
7424 send_message_type(4, user, cmd->parent->bot, "%s", eb);
7425 return 1;
7426}
7427#endif
7428
d76ed9a9
AS
7429static CHANSERV_FUNC(cmd_d)
7430{
7431 unsigned long sides, count, modifier, ii, total;
7432 char response[MAXLEN], *sep;
7433 const char *fmt;
7434
7435 REQUIRE_PARAMS(2);
7436 if((count = strtoul(argv[1], &sep, 10)) < 1)
7437 goto no_dice;
7438 if(sep[0] == 0)
7439 {
7440 if(count == 1)
7441 goto no_dice;
7442 sides = count;
7443 count = 1;
7444 modifier = 0;
7445 }
7446 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7447 && (sides = strtoul(sep+1, &sep, 10)) > 1)
7448 {
7449 if(sep[0] == 0)
7450 modifier = 0;
7451 else if((sep[0] == '-') && isdigit(sep[1]))
7452 modifier = strtoul(sep, NULL, 10);
7453 else if((sep[0] == '+') && isdigit(sep[1]))
7454 modifier = strtoul(sep+1, NULL, 10);
7455 else
7456 goto no_dice;
7457 }
7458 else
7459 {
7460 no_dice:
7461 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7462 return 0;
7463 }
7464 if(count > 10)
7465 {
7466 reply("CSMSG_BAD_DICE_COUNT", count, 10);
7467 return 0;
7468 }
7469 for(total = ii = 0; ii < count; ++ii)
7470 total += (rand() % sides) + 1;
7471 total += modifier;
7472
7473 if((count > 1) || modifier)
7474 {
7475 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7476 sprintf(response, fmt, total, count, sides, modifier);
7477 }
7478 else
7479 {
7480 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7481 sprintf(response, fmt, total, sides);
7482 }
7483 if(channel)
7484 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7485 else
7486 send_message_type(4, user, cmd->parent->bot, "%s", response);
7487 return 1;
7488}
7489
7490static CHANSERV_FUNC(cmd_huggle)
7491{
7492 /* CTCP must be via PRIVMSG, never notice */
7493 if(channel)
7494 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7495 else
7496 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7497 return 1;
7498}
7499
e5a8f7cd 7500static CHANSERV_FUNC(cmd_calc)
7501{
7502 char response[MAXLEN];
7503
7504 REQUIRE_PARAMS(2);
7505 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
7506
7507 if(channel)
d8cf9c21 7508 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
e5a8f7cd 7509 else
7510 send_message_type(4, user, cmd->parent->bot, "%s", response);
7511 return 1;
7512}
7513
33741441
AS
7514static CHANSERV_FUNC(cmd_reply)
7515{
7516
7517 REQUIRE_PARAMS(2);
7518 unsplit_string(argv + 1, argc - 1, NULL);
7519
7520 if(channel)
7521 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, unsplit_string(argv + 1, argc - 1, NULL));
7522 else
7523 send_message_type(4, user, cmd->parent->bot, "%s", unsplit_string(argv + 1, argc - 1, NULL));
7524 return 1;
7525}
7526
d76ed9a9
AS
7527static void
7528chanserv_adjust_limit(void *data)
7529{
7530 struct mod_chanmode change;
7531 struct chanData *cData = data;
7532 struct chanNode *channel = cData->channel;
7533 unsigned int limit;
7534
7535 if(IsSuspended(cData))
7536 return;
7537
7538 cData->limitAdjusted = now;
7539 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7540 if(cData->modes.modes_set & MODE_LIMIT)
7541 {
7542 if(limit > cData->modes.new_limit)
7543 limit = cData->modes.new_limit;
7544 else if(limit == cData->modes.new_limit)
7545 return;
7546 }
7547
7548 mod_chanmode_init(&change);
7549 change.modes_set = MODE_LIMIT;
7550 change.new_limit = limit;
7551 mod_chanmode_announce(chanserv, channel, &change);
7552}
7553
7554static void
7555handle_new_channel(struct chanNode *channel)
7556{
7557 struct chanData *cData;
7558
7559 if(!(cData = channel->channel_info))
7560 return;
7561
7562 if(cData->modes.modes_set || cData->modes.modes_clear)
7563 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7564
7565 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7fda2b52 7566 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
d76ed9a9
AS
7567}
7568
5aa400d2 7569int
7570trace_check_bans(struct userNode *user, struct chanNode *chan)
7571{
5aa400d2 7572 struct banData *bData;
27fa6acf 7573 struct mod_chanmode *change;
5aa400d2 7574
27fa6acf 7575 change = find_matching_bans(&chan->banlist, user, NULL);
7576 if (change)
7577 return 1;
7578
27fa6acf 7579 /* lamer list */
1e993296 7580 if (chan->channel_info) {
7581 for(bData = chan->channel_info->bans; bData; bData = bData->next) {
5aa400d2 7582
1e993296 7583 if(!user_matches_glob(user, bData->mask, MATCH_USENICK))
7584 continue;
7585
7586 if(bData)
7587 return 1;
5aa400d2 7588 }
7589 }
7590
7591 return 0;
7592}
7593
c99dcaf6 7594int
1b6b5e2f 7595check_bans(struct userNode *user, const char *channel)
c99dcaf6 7596{
7597 struct chanNode *chan;
7598 struct mod_chanmode change;
7599 struct chanData *cData;
7600 struct banData *bData;
7601
7602 if (!(chan = GetChannel(channel)))
7603 return 0;
7604
7605 if(!(cData = chan->channel_info))
7606 return 0;
7607
1b6b5e2f 7608 mod_chanmode_init(&change);
7609 change.argc = 1;
7610
c99dcaf6 7611 if(chan->banlist.used < MAXBANS)
7612 {
7613 /* Not joining through a ban. */
7614 for(bData = cData->bans;
7615 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7616 bData = bData->next);
7617
7618 if(bData)
7619 {
7620 char kick_reason[MAXLEN];
7621 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7622
7623 bData->triggered = now;
7624 if(bData != cData->bans)
7625 {
7626 /* Shuffle the ban to the head of the list. */
7627 if(bData->next)
7628 bData->next->prev = bData->prev;
7629 if(bData->prev)
7630 bData->prev->next = bData->next;
7631
7632 bData->prev = NULL;
7633 bData->next = cData->bans;
7634
7635 if(cData->bans)
7636 cData->bans->prev = bData;
7637
7638 cData->bans = bData;
7639 }
7640
7641 change.args[0].mode = MODE_BAN;
7642 change.args[0].u.hostmask = bData->mask;
7643 mod_chanmode_announce(chanserv, chan, &change);
7644 KickChannelUser(user, chan, chanserv, kick_reason);
7645 return 1;
7646 }
7647 }
7648 return 0;
7649}
7650
5aef35cf
AS
7651int
7652channel_user_is_exempt(struct userNode *user, struct chanNode *channel)
7653{
7654 unsigned int ii;
7655 for(ii = 0; ii < channel->exemptlist.used; ii++)
7656 {
7657 if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK))
7658 return true;
7659 }
7660 return false;
7661}
7662
c99dcaf6 7663
d76ed9a9
AS
7664/* Welcome to my worst nightmare. Warning: Read (or modify)
7665 the code below at your own risk. */
7666static int
7667handle_join(struct modeNode *mNode)
7668{
7669 struct mod_chanmode change;
7670 struct userNode *user = mNode->user;
7671 struct chanNode *channel = mNode->channel;
7672 struct chanData *cData;
7673 struct userData *uData = NULL;
7674 struct banData *bData;
7675 struct handle_info *handle;
7676 unsigned int modes = 0, info = 0;
7677 char *greeting;
7678
ed7ac86b 7679 if(IsLocal(user) || !channel || !channel->channel_info || IsSuspended(channel->channel_info))
d76ed9a9
AS
7680 return 0;
7681
7682 cData = channel->channel_info;
7683 if(channel->members.used > cData->max)
7684 cData->max = channel->members.used;
7685
12673a59 7686#ifdef notdef
2f61d1d7 7687 /* Check for bans. If they're joining through a ban, one of two
7688 * cases applies:
7689 * 1: Join during a netburst, by riding the break. Kick them
7690 * unless they have ops or voice in the channel.
7691 * 2: They're allowed to join through the ban (an invite in
7692 * ircu2.10, or a +e on Hybrid, or something).
7693 * If they're not joining through a ban, and the banlist is not
7694 * full, see if they're on the banlist for the channel. If so,
7695 * kickban them.
7696 */
7697 if(user->uplink->burst && !mNode->modes)
7698 {
7699 unsigned int ii;
7700 for(ii = 0; ii < channel->banlist.used; ii++)
7701 {
7702 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7703 {
7704 /* Riding a netburst. Naughty. */
7705 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7706 return 1;
7707 }
7708 }
7709 }
12673a59 7710#endif
2f61d1d7 7711
ff5f1ab2
AS
7712 if(user->handle_info)
7713 {
7714 handle = user->handle_info;
7715 if(handle)
7716 {
7717 uData = GetTrueChannelAccess(cData, handle);
7718 }
7719 }
7720
7721
d76ed9a9
AS
7722 mod_chanmode_init(&change);
7723 change.argc = 1;
d76ed9a9 7724
ff5f1ab2
AS
7725 /* TODO: maybe only people above inviteme level? -Rubin */
7726 /* We don't kick people with access */
5aef35cf 7727 if(!uData && !channel_user_is_exempt(user, channel))
ff5f1ab2
AS
7728 {
7729 if(channel->banlist.used < MAXBANS)
d76ed9a9 7730 {
ff5f1ab2
AS
7731 /* Not joining through a ban. */
7732 for(bData = cData->bans;
7733 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7734 bData = bData->next);
d76ed9a9 7735
ff5f1ab2 7736 if(bData)
d76ed9a9 7737 {
ff5f1ab2
AS
7738 char kick_reason[MAXLEN];
7739 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
d76ed9a9 7740
ff5f1ab2
AS
7741 bData->triggered = now;
7742 if(bData != cData->bans)
7743 {
7744 /* Shuffle the ban to the head of the list. */
7745 if(bData->next)
7746 bData->next->prev = bData->prev;
7747 if(bData->prev)
7748 bData->prev->next = bData->next;
7749
7750 bData->prev = NULL;
7751 bData->next = cData->bans;
7752
7753 if(cData->bans)
7754 cData->bans->prev = bData;
7755 cData->bans = bData;
7756 }
7757
7758 change.args[0].mode = MODE_BAN;
7759 change.args[0].u.hostmask = bData->mask;
7760 mod_chanmode_announce(chanserv, channel, &change);
7761 KickChannelUser(user, channel, chanserv, kick_reason);
7762 return 1;
7763 }
d76ed9a9
AS
7764 }
7765 }
7766
7767 /* ChanServ will not modify the limits in join-flooded channels.
7768 It will also skip DynLimit processing when the user (or srvx)
7769 is bursting in, because there are likely more incoming. */
7770 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7771 && !user->uplink->burst
7772 && !channel->join_flooded
7773 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7774 {
7775 /* The user count has begun "bumping" into the channel limit,
7776 so set a timer to raise the limit a bit. Any previous
7777 timers are removed so three incoming users within the delay
7778 results in one limit change, not three. */
7779
7780 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7781 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7782 }
7783
4b6129c0
AS
7784 /* Give automodes exept during join-floods */
7785 if(!channel->join_flooded)
d76ed9a9 7786 {
4b6129c0
AS
7787 if(cData->chOpts[chAutomode] == 'v')
7788 modes |= MODE_VOICE;
7789 else if(cData->chOpts[chAutomode] == 'h')
7790 modes |= MODE_HALFOP;
7791 else if(cData->chOpts[chAutomode] == 'o')
7792 modes |= MODE_CHANOP;
d76ed9a9 7793 }
c8ca69a0 7794
d76ed9a9
AS
7795 greeting = cData->greeting;
7796 if(user->handle_info)
7797 {
ff5f1ab2 7798/* handle = user->handle_info; */
d76ed9a9
AS
7799
7800 if(IsHelper(user) && !IsHelping(user))
7801 {
7802 unsigned int ii;
7803 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7804 {
7805 if(channel == chanserv_conf.support_channels.list[ii])
7806 {
7807 HANDLE_SET_FLAG(user->handle_info, HELPING);
7808 break;
7809 }
7810 }
7811 }
7812
ff5f1ab2 7813/* uData = GetTrueChannelAccess(cData, handle); */
d76ed9a9
AS
7814 if(uData && !IsUserSuspended(uData))
7815 {
4b6129c0
AS
7816 /* non users getting automodes are handled above. */
7817 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 7818 {
4d69a3b1
AS
7819 /* just op everyone with access */
7820 if(uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
7821 modes |= MODE_VOICE;
7822 /* or do their access level */
7823 else if(uData->access >= UL_OP )
d76ed9a9 7824 modes |= MODE_CHANOP;
4b6129c0 7825 else if(uData->access >= UL_HALFOP )
55342ce8 7826 modes |= MODE_HALFOP;
4b6129c0 7827 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
d76ed9a9
AS
7828 modes |= MODE_VOICE;
7829 }
7830 if(uData->access >= UL_PRESENT)
7831 cData->visited = now;
7832 if(cData->user_greeting)
7833 greeting = cData->user_greeting;
7834 if(uData->info
7835 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7836 && ((now - uData->seen) >= chanserv_conf.info_delay)
7837 && !uData->present)
7838 info = 1;
7839 uData->seen = now;
7840 uData->present = 1;
7841 }
7842 }
2f61d1d7 7843
7844 /* If user joining normally (not during burst), apply op or voice,
7845 * and send greeting/userinfo as appropriate.
7846 */
d76ed9a9
AS
7847 if(!user->uplink->burst)
7848 {
7849 if(modes)
7850 {
4b6129c0 7851 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
55342ce8 7852 if(modes & MODE_CHANOP) {
7853 modes &= ~MODE_HALFOP;
d76ed9a9 7854 modes &= ~MODE_VOICE;
55342ce8 7855 }
4b6129c0 7856 */
d76ed9a9 7857 change.args[0].mode = modes;
a32da4c7 7858 change.args[0].u.member = mNode;
d76ed9a9
AS
7859 mod_chanmode_announce(chanserv, channel, &change);
7860 }
2f61d1d7 7861 if(greeting)
d76ed9a9
AS
7862 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7863 if(uData && info)
7864 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7865 }
7866 return 0;
7867}
7868
dd019452 7869static void
ffb204b6 7870chanserv_autojoin_channels(struct userNode *user)
dd019452 7871{
7872 struct userData *channel;
dd019452 7873
7874 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7875 {
7876 struct chanNode *cn;
7877 struct modeNode *mn;
7878
7879 if(IsUserSuspended(channel)
7880 || IsSuspended(channel->channel)
7881 || !(cn = channel->channel->channel))
7882 continue;
7883
7884 mn = GetUserMode(cn, user);
7885 if(!mn)
7886 {
7887 if(!IsUserSuspended(channel)
7888 && IsUserAutoJoin(channel)
7889 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7890 && !self->burst
7891 && !user->uplink->burst)
7892 irc_svsjoin(chanserv, user, cn);
7893 }
7894 }
7895}
7896
d76ed9a9
AS
7897static void
7898handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7899{
7900 struct mod_chanmode change;
7901 struct userData *channel;
5177fd21 7902 unsigned int ii, jj, i;
d76ed9a9
AS
7903
7904 if(!user->handle_info)
7905 return;
7906
7907 mod_chanmode_init(&change);
7908 change.argc = 1;
7909 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7910 {
7911 struct chanNode *cn;
10fb34f6 7912 struct chanData *cData;
d76ed9a9
AS
7913 struct modeNode *mn;
7914 if(IsUserSuspended(channel)
7915 || IsSuspended(channel->channel)
7916 || !(cn = channel->channel->channel))
7917 continue;
7918
10fb34f6 7919 cData = cn->channel_info;
d76ed9a9
AS
7920 mn = GetUserMode(cn, user);
7921 if(!mn)
7922 {
7923 if(!IsUserSuspended(channel)
7924 && IsUserAutoInvite(channel)
7925 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7926 && !self->burst
7927 && !user->uplink->burst)
7928 irc_invite(chanserv, user, cn);
7929 continue;
7930 }
7931
7932 if(channel->access >= UL_PRESENT)
7933 channel->channel->visited = now;
7934
10fb34f6 7935 if(IsUserAutoOp(channel) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 7936 {
4b6129c0 7937 if(channel->access >= UL_OP )
d76ed9a9 7938 change.args[0].mode = MODE_CHANOP;
4b6129c0 7939 else if(channel->access >= UL_HALFOP )
55342ce8 7940 change.args[0].mode = MODE_HALFOP;
4b6129c0 7941 else if(channel->access >= UL_PEON )
d76ed9a9
AS
7942 change.args[0].mode = MODE_VOICE;
7943 else
7944 change.args[0].mode = 0;
a32da4c7 7945 change.args[0].u.member = mn;
d76ed9a9
AS
7946 if(change.args[0].mode)
7947 mod_chanmode_announce(chanserv, cn, &change);
7948 }
7949
7950 channel->seen = now;
7951 channel->present = 1;
7952 }
7953
7954 for(ii = 0; ii < user->channels.used; ++ii)
7955 {
7956 struct chanNode *channel = user->channels.list[ii]->channel;
7957 struct banData *ban;
7958
55342ce8 7959 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
1117fc5a 7960 || !channel->channel_info
7961 || IsSuspended(channel->channel_info))
d76ed9a9 7962 continue;
ff5f1ab2
AS
7963 if(protect_user(user, chanserv, channel->channel_info, true))
7964 continue;
d76ed9a9 7965 for(jj = 0; jj < channel->banlist.used; ++jj)
2f61d1d7 7966 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
d76ed9a9
AS
7967 break;
7968 if(jj < channel->banlist.used)
7969 continue;
7970 for(ban = channel->channel_info->bans; ban; ban = ban->next)
7971 {
7972 char kick_reason[MAXLEN];
2f61d1d7 7973 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9
AS
7974 continue;
7975 change.args[0].mode = MODE_BAN;
a32da4c7 7976 change.args[0].u.hostmask = ban->mask;
d76ed9a9
AS
7977 mod_chanmode_announce(chanserv, channel, &change);
7978 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7979 KickChannelUser(user, channel, chanserv, kick_reason);
7980 ban->triggered = now;
7981 break;
7982 }
7983 }
7984
7985 if(IsSupportHelper(user))
7986 {
7987 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7988 {
7989 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7990 {
7991 HANDLE_SET_FLAG(user->handle_info, HELPING);
7992 break;
7993 }
7994 }
7995 }
5177fd21 7996
7997 if (user->handle_info->ignores->used) {
7998 for (i=0; i < user->handle_info->ignores->used; i++) {
7999 irc_silence(user, user->handle_info->ignores->list[i], 1);
8000 }
8001 }
56958740 8002
8003 if (user->handle_info->epithet)
8004 irc_swhois(chanserv, user, user->handle_info->epithet);
dd019452 8005
8006 /* process autojoin channels 5 seconds later as this sometimes
8007 happens before autohide */
ffb204b6
AS
8008// timeq_add(now + 5, chanserv_autojoin_channels, user);
8009 chanserv_autojoin_channels(user);
d76ed9a9
AS
8010}
8011
8012static void
8013handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
8014{
8015 struct chanData *cData;
8016 struct userData *uData;
8017
8018 cData = mn->channel->channel_info;
8019 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
8020 return;
8021
8022 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
8023 {
8024 /* Allow for a bit of padding so that the limit doesn't
8025 track the user count exactly, which could get annoying. */
8026 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
8027 {
8028 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8029 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8030 }
8031 }
8032
8033 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
8034 {
8035 scan_user_presence(uData, mn->user);
8036 uData->seen = now;
8037 }
8038
8039 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
8040 {
8041 unsigned int ii, jj;
8042 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8043 {
8044 for(jj = 0; jj < mn->user->channels.used; ++jj)
8045 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
8046 break;
8047 if(jj < mn->user->channels.used)
8048 break;
8049 }
8050 if(ii == chanserv_conf.support_channels.used)
8051 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
8052 }
8053}
8054
8055static void
8056handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
8057{
8058 struct userData *uData;
8059
8060 if(!channel->channel_info || !kicker || IsService(kicker)
8061 || (kicker == victim) || IsSuspended(channel->channel_info)
8062 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
8063 return;
8064
ff5f1ab2 8065 if(protect_user(victim, kicker, channel->channel_info, false))
d76ed9a9 8066 {
31f23f13 8067 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
d76ed9a9
AS
8068 KickChannelUser(kicker, channel, chanserv, reason);
8069 }
8070
8071 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
8072 uData->seen = now;
8073}
8074
8075static int
8076handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
8077{
8078 struct chanData *cData;
8079
8080 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
8081 return 0;
8082
8083 cData = channel->channel_info;
8084 if(bad_topic(channel, user, channel->topic))
b75e24a3 8085 { /* User doesnt have privs to set topics. Undo it */
d76ed9a9 8086 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7fda2b52 8087 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
d76ed9a9
AS
8088 return 1;
8089 }
b75e24a3
AS
8090 /* If there is a topic mask set, and the new topic doesnt match,
8091 * set the topic to mask + new_topic */
8092 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
8093 {
8094 char new_topic[TOPICLEN+1];
8095 conform_topic(cData->topic_mask, channel->topic, new_topic);
8096 if(*new_topic)
8097 {
75ef8cdc 8098 SetChannelTopic(channel, chanserv, user, new_topic, 1);
b75e24a3
AS
8099 /* and fall through to topicsnarf code below.. */
8100 }
8101 else /* Topic couldnt fit into mask, was too long */
8102 {
75ef8cdc 8103 SetChannelTopic(channel, chanserv, user, old_topic, 1);
b75e24a3
AS
8104 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
8105 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
8106 return 1;
8107 }
8108 }
d76ed9a9
AS
8109 /* With topicsnarf, grab the topic and save it as the default topic. */
8110 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
8111 {
8112 free(cData->topic);
8113 cData->topic = strdup(channel->topic);
8114 }
8115 return 0;
8116}
8117
8118static void
8119handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
8120{
8121 struct mod_chanmode *bounce = NULL;
8122 unsigned int bnc, ii;
8123 char deopped = 0;
8124
8125 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
8126 return;
8127
8128 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
8129 && mode_lock_violated(&channel->channel_info->modes, change))
8130 {
8131 char correct[MAXLEN];
8132 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
8133 mod_chanmode_format(&channel->channel_info->modes, correct);
8134 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
8135 }
8136 for(ii = bnc = 0; ii < change->argc; ++ii)
8137 {
8138 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
8139 {
a32da4c7 8140 const struct userNode *victim = change->args[ii].u.member->user;
ff5f1ab2 8141 if(!protect_user(victim, user, channel->channel_info, false))
d76ed9a9
AS
8142 continue;
8143 if(!bounce)
8144 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8145 if(!deopped)
8146 {
8147 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 8148 bounce->args[bnc].u.member = GetUserMode(channel, user);
8149 if(bounce->args[bnc].u.member)
d76ed9a9
AS
8150 bnc++;
8151 deopped = 1;
8152 }
8153 bounce->args[bnc].mode = MODE_CHANOP;
a32da4c7 8154 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9
AS
8155 bnc++;
8156 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
8157 }
8158 else if(change->args[ii].mode & MODE_CHANOP)
8159 {
a32da4c7 8160 const struct userNode *victim = change->args[ii].u.member->user;
c092fcad 8161 if(IsService(victim) || validate_op(NULL, user, channel, (struct userNode*)victim))
d76ed9a9
AS
8162 continue;
8163 if(!bounce)
8164 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8165 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 8166 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9
AS
8167 bnc++;
8168 }
8169 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
8170 {
a32da4c7 8171 const char *ban = change->args[ii].u.hostmask;
d76ed9a9
AS
8172 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
8173 continue;
8174 if(!bounce)
8175 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8176 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 8177 bounce->args[bnc].u.hostmask = strdup(ban);
d76ed9a9
AS
8178 bnc++;
8179 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
8180 }
8181 }
8182 if(bounce)
8183 {
8184 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
8185 mod_chanmode_announce(chanserv, channel, bounce);
ec1a68c8 8186 for(ii = 0; ii < change->argc; ++ii)
8187 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
8188 free((char*)bounce->args[ii].u.hostmask);
d76ed9a9
AS
8189 mod_chanmode_free(bounce);
8190 }
8191}
8192
8193static void
8194handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
8195{
8196 struct chanNode *channel;
8197 struct banData *bData;
8198 struct mod_chanmode change;
8199 unsigned int ii, jj;
8200 char kick_reason[MAXLEN];
8201
8202 mod_chanmode_init(&change);
8203 change.argc = 1;
8204 change.args[0].mode = MODE_BAN;
8205 for(ii = 0; ii < user->channels.used; ++ii)
8206 {
8207 channel = user->channels.list[ii]->channel;
8208 /* Need not check for bans if they're opped or voiced. */
4b6129c0
AS
8209 /* TODO: does this make sense in automode v, h, and o? *
8210 * lets still enforce on voice people anyway, and see how that goes -Rubin */
ff5f1ab2 8211 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE ))
d76ed9a9
AS
8212 continue;
8213 /* Need not check for bans unless channel registration is active. */
8214 if(!channel->channel_info || IsSuspended(channel->channel_info))
8215 continue;
8216 /* Look for a matching ban already on the channel. */
8217 for(jj = 0; jj < channel->banlist.used; ++jj)
2f61d1d7 8218 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
d76ed9a9
AS
8219 break;
8220 /* Need not act if we found one. */
8221 if(jj < channel->banlist.used)
8222 continue;
ff5f1ab2
AS
8223 /* don't kick someone on the userlist */
8224 if(protect_user(user, chanserv, channel->channel_info, true))
8225 continue;
d76ed9a9
AS
8226 /* Look for a matching ban in this channel. */
8227 for(bData = channel->channel_info->bans; bData; bData = bData->next)
8228 {
2f61d1d7 8229 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9 8230 continue;
a32da4c7 8231 change.args[0].u.hostmask = bData->mask;
d76ed9a9
AS
8232 mod_chanmode_announce(chanserv, channel, &change);
8233 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8234 KickChannelUser(user, channel, chanserv, kick_reason);
8235 bData->triggered = now;
8236 break; /* we don't need to check any more bans in the channel */
8237 }
8238 }
8239}
8240
8241static void handle_rename(struct handle_info *handle, const char *old_handle)
8242{
8243 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
8244
8245 if(dnr)
8246 {
8247 dict_remove2(handle_dnrs, old_handle, 1);
8248 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
8249 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
8250 }
8251}
8252
8253static void
8254handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
8255{
8256 struct userNode *h_user;
8257
8258 if(handle->channels)
8259 {
8260 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
8261 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
8262
8263 while(handle->channels)
8264 del_channel_user(handle->channels, 1);
8265 }
8266}
8267
8268static void
8269handle_server_link(UNUSED_ARG(struct server *server))
8270{
8271 struct chanData *cData;
8272
8273 for(cData = channelList; cData; cData = cData->next)
8274 {
8275 if(!IsSuspended(cData))
8276 cData->may_opchan = 1;
8277 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
8278 && !cData->channel->join_flooded
8279 && ((cData->channel->limit - cData->channel->members.used)
8280 < chanserv_conf.adjust_threshold))
8281 {
8282 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8283 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8284 }
8285 }
8286}
8287
8288static void
8289chanserv_conf_read(void)
8290{
8291 dict_t conf_node;
8292 const char *str;
8293 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
8294 struct mod_chanmode *change;
8295 struct string_list *strlist;
8296 struct chanNode *chan;
8297 unsigned int ii;
8298
8299 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
8300 {
8301 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
8302 return;
8303 }
8304 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8305 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8306 chanserv_conf.support_channels.used = 0;
8307 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
8308 {
8309 for(ii = 0; ii < strlist->used; ++ii)
8310 {
8311 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
8312 if(!str2)
8313 str2 = "+nt";
2aef5f4b 8314 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
d76ed9a9
AS
8315 LockChannel(chan);
8316 channelList_append(&chanserv_conf.support_channels, chan);
8317 }
8318 }
8319 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
8320 {
8321 const char *str2;
8322 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
8323 if(!str2)
8324 str2 = "+nt";
2aef5f4b 8325 chan = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9
AS
8326 LockChannel(chan);
8327 channelList_append(&chanserv_conf.support_channels, chan);
8328 }
8329 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
8330 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
8331 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
8332 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
8333 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
1117fc5a 8334 chanserv_conf.greeting_length = str ? atoi(str) : 200;
d76ed9a9
AS
8335 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
8336 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
8337 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
8338 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
8339 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
8340 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
31f23f13
AS
8341 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
8342 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
d76ed9a9
AS
8343 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
8344 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
8345 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
8346 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
8347 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
8348 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
8349 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
8350 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
8351 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
8352 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
8353 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
8354 if(chanserv && str)
8355 NickChange(chanserv, str, 0);
8356 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
8357 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
a32da4c7 8358 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
8359 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
d76ed9a9
AS
8360 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
8361 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
8362 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
8363 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
8364 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
8365 chanserv_conf.max_owned = str ? atoi(str) : 5;
8366 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
8367 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
8368 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
8369 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
8370 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
8371 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7637f48f 8372 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
8373 god_timeout = str ? ParseInterval(str) : 60*15;
d76ed9a9
AS
8374 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
8375 if(!str)
8376 str = "+nt";
8377 safestrncpy(mode_line, str, sizeof(mode_line));
8378 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
2f61d1d7 8379 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
8380 && (change->argc < 2))
d76ed9a9
AS
8381 {
8382 chanserv_conf.default_modes = *change;
8383 mod_chanmode_free(change);
8384 }
8385 free_string_list(chanserv_conf.set_shows);
8386 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
8387 if(strlist)
8388 strlist = string_list_copy(strlist);
8389 else
8390 {
8391 static const char *list[] = {
8392 /* free form text */
8393 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
8394 /* options based on user level */
4b6129c0
AS
8395 "PubCmd", "InviteMe", "UserInfo","EnfOps",
8396 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
d76ed9a9 8397 /* multiple choice options */
d3347099 8398 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
d76ed9a9 8399 /* binary options */
31f23f13 8400 "DynLimit", "NoDelete", "BanTimeout",
d76ed9a9
AS
8401 /* delimiter */
8402 NULL
8403 };
8404 unsigned int ii;
8405 strlist = alloc_string_list(ArrayLength(list)-1);
8406 for(ii=0; list[ii]; ii++)
8407 string_list_append(strlist, strdup(list[ii]));
8408 }
8409 chanserv_conf.set_shows = strlist;
8410 /* We don't look things up now, in case the list refers to options
8411 * defined by modules initialized after this point. Just mark the
8412 * function list as invalid, so it will be initialized.
8413 */
8414 set_shows_list.used = 0;
8415 free_string_list(chanserv_conf.eightball);
8416 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
8417 if(strlist)
8418 {
8419 strlist = string_list_copy(strlist);
8420 }
8421 else
8422 {
8423 strlist = alloc_string_list(4);
8424 string_list_append(strlist, strdup("Yes."));
8425 string_list_append(strlist, strdup("No."));
8426 string_list_append(strlist, strdup("Maybe so."));
8427 }
8428 chanserv_conf.eightball = strlist;
8429 free_string_list(chanserv_conf.old_ban_names);
8430 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
8431 if(strlist)
8432 strlist = string_list_copy(strlist);
8433 else
8434 strlist = alloc_string_list(2);
8435 chanserv_conf.old_ban_names = strlist;
d76ed9a9 8436 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
1117fc5a 8437 off_channel = str ? atoi(str) : 0;
d76ed9a9
AS
8438}
8439
8440static void
8441chanserv_note_type_read(const char *key, struct record_data *rd)
8442{
8443 dict_t obj;
8444 struct note_type *ntype;
8445 const char *str;
8446
8447 if(!(obj = GET_RECORD_OBJECT(rd)))
8448 {
8449 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8450 return;
8451 }
8452 if(!(ntype = chanserv_create_note_type(key)))
8453 {
8454 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8455 return;
8456 }
8457
8458 /* Figure out set access */
8459 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8460 {
8461 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8462 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8463 }
8464 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8465 {
8466 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8467 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8468 }
8469 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8470 {
8471 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8472 }
8473 else
8474 {
8475 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8476 ntype->set_access_type = NOTE_SET_PRIVILEGED;
8477 ntype->set_access.min_opserv = 0;
8478 }
8479
8480 /* Figure out visibility */
8481 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8482 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8483 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8484 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8485 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8486 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8487 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8488 ntype->visible_type = NOTE_VIS_ALL;
8489 else
8490 ntype->visible_type = NOTE_VIS_PRIVILEGED;
8491
8492 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8493 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8494}
8495
8496static void
8497user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8498{
8499 struct handle_info *handle;
8500 struct userData *uData;
dfaa28a4 8501 char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc;
d76ed9a9 8502 time_t last_seen;
dfaa28a4 8503 unsigned short access, lastaccess = 0;
d76ed9a9
AS
8504
8505 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8506 {
8507 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8508 return;
8509 }
8510
8511 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8512 if(access > UL_OWNER)
8513 {
8514 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8515 return;
8516 }
8517
8518 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8519 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8520 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
8521 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
b10abdb2 8522 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
dfaa28a4 8523 accessexpiry = database_get_data(rd->d.object, KEY_ACCESSEXPIRY, RECDB_QSTRING);
8524 clvlexpiry = database_get_data(rd->d.object, KEY_CLVLEXPIRY, RECDB_QSTRING);
8525 lstacc = database_get_data(rd->d.object, KEY_LASTLEVEL, RECDB_QSTRING);
8526 lastaccess = lstacc ? atoi(lstacc) : 0;
8527
d76ed9a9
AS
8528 handle = get_handle_info(key);
8529 if(!handle)
8530 {
8531 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8532 return;
8533 }
8534
fd20b142 8535 uData = add_channel_user(chan, handle, access, last_seen, inf, 0);
d76ed9a9 8536 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
b10abdb2 8537 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
dfaa28a4 8538
8539 uData->accessexpiry = accessexpiry ? strtoul(accessexpiry, NULL, 0) : 0;
8540 if (uData->accessexpiry > 0)
8541 timeq_add(uData->accessexpiry, chanserv_expire_tempuser, uData);
8542
8543 uData->clvlexpiry = clvlexpiry ? strtoul(clvlexpiry, NULL, 0) : 0;
8544 if (uData->clvlexpiry > 0)
8545 timeq_add(uData->clvlexpiry, chanserv_expire_tempclvl, uData);
8546
8547 uData->lastaccess = lastaccess;
b10abdb2 8548
8549 if((uData->flags & USER_SUSPENDED) && uData->expires)
8550 {
8551 if(uData->expires > now)
8552 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8553 else
8554 uData->flags &= ~USER_SUSPENDED;
8555 }
c8ca69a0
AS
8556
8557 /* Upgrade: set autoop to the inverse of noautoop */
8558 if(chanserv_read_version < 2)
8559 {
8560 /* if noautoop is true, set autoop false, and vice versa */
8561 if(uData->flags & USER_NOAUTO_OP)
8562 uData->flags = uData->flags & ~USER_AUTO_OP;
8563 else
8564 uData->flags = uData->flags | USER_AUTO_OP;
8565 log_module(CS_LOG, LOG_INFO, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version, uData->flags, key, chan->channel->name);
8566 }
8567
d76ed9a9
AS
8568}
8569
8570static void
8571ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8572{
8573 struct banData *bData;
8574 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8575 time_t set_time, triggered_time, expires_time;
8576
8577 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8578 {
8579 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8580 return;
8581 }
8582
8583 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8584 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8585 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8586 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8587 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8588 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
0d16e639 8589 if (!reason || !owner)
8590 return;
d76ed9a9
AS
8591
8592 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
8593 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
8594 if(s_expires)
8595 expires_time = (time_t)strtoul(s_expires, NULL, 0);
8596 else if(s_duration)
8597 expires_time = set_time + atoi(s_duration);
8598 else
8599 expires_time = 0;
8600
ec1a68c8 8601 if(!reason || (expires_time && (expires_time < now)))
d76ed9a9
AS
8602 return;
8603
8604 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8605}
8606
8607static struct suspended *
8608chanserv_read_suspended(dict_t obj)
8609{
8610 struct suspended *suspended = calloc(1, sizeof(*suspended));
8611 char *str;
8612 dict_t previous;
8613
8614 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8615 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
8616 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8617 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
8618 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8619 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8620 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8621 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8622 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8623 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8624 return suspended;
8625}
8626
82f37c08 8627static struct giveownership *
8628chanserv_read_giveownership(dict_t obj)
8629{
8630 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8631 char *str;
8632 dict_t previous;
8633
8634 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8635 giveownership->staff_issuer = str ? strdup(str) : NULL;
8636
8637 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8638
8639 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8640 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8641
8642 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8643 giveownership->reason = str ? strdup(str) : NULL;
8644 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8645 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8646
8647 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8648 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8649 return giveownership;
8650}
8651
d76ed9a9
AS
8652static int
8653chanserv_channel_read(const char *key, struct record_data *hir)
8654{
8655 struct suspended *suspended;
82f37c08 8656 struct giveownership *giveownership;
d76ed9a9
AS
8657 struct mod_chanmode *modes;
8658 struct chanNode *cNode;
8659 struct chanData *cData;
8660 struct dict *channel, *obj;
8661 char *str, *argv[10];
8662 dict_iterator_t it;
8663 unsigned int argc;
8664
8665 channel = hir->d.object;
8666
8667 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8668 if(!str)
8669 str = "<unknown>";
2aef5f4b 8670 cNode = AddChannel(key, now, NULL, NULL, NULL);
d76ed9a9
AS
8671 if(!cNode)
8672 {
8673 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8674 return 0;
8675 }
8676 cData = register_channel(cNode, str);
8677 if(!cData)
8678 {
8679 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8680 return 0;
8681 }
8682
8683 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8684 {
8685 enum levelOption lvlOpt;
8686 enum charOption chOpt;
8687
8688 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8689 cData->flags = atoi(str);
8690
8691 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8692 {
8693 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8694 if(str)
8695 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8696 else if(levelOptions[lvlOpt].old_flag)
8697 {
8698 if(cData->flags & levelOptions[lvlOpt].old_flag)
8699 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8700 else
8701 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8702 }
8703 }
8704
8705 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8706 {
8707 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8708 continue;
8709 cData->chOpts[chOpt] = str[0];
8710 }
8711 }
8712 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8713 {
8714 enum levelOption lvlOpt;
8715 enum charOption chOpt;
8716 unsigned int count;
8717
8718 cData->flags = base64toint(str, 5);
8719 count = strlen(str += 5);
8720 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8721 {
8722 unsigned short lvl;
8723 if(levelOptions[lvlOpt].old_flag)
8724 {
8725 if(cData->flags & levelOptions[lvlOpt].old_flag)
8726 lvl = levelOptions[lvlOpt].flag_value;
8727 else
8728 lvl = levelOptions[lvlOpt].default_value;
8729 }
8730 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8731 {
8732 case 'c': lvl = UL_COOWNER; break;
4048352e 8733 case 'm': lvl = UL_MANAGER; break;
d76ed9a9
AS
8734 case 'n': lvl = UL_OWNER+1; break;
8735 case 'o': lvl = UL_OP; break;
8736 case 'p': lvl = UL_PEON; break;
55342ce8 8737 case 'h': lvl = UL_HALFOP; break;
d76ed9a9
AS
8738 case 'w': lvl = UL_OWNER; break;
8739 default: lvl = 0; break;
8740 }
8741 cData->lvlOpts[lvlOpt] = lvl;
8742 }
8743 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8744 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8745 }
8746
8747 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8748 {
8749 suspended = chanserv_read_suspended(obj);
8750 cData->suspended = suspended;
8751 suspended->cData = cData;
8752 /* We could use suspended->expires and suspended->revoked to
8753 * set the CHANNEL_SUSPENDED flag, but we don't. */
8754 }
a32da4c7 8755 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
d76ed9a9
AS
8756 {
8757 suspended = calloc(1, sizeof(*suspended));
8758 suspended->issued = 0;
8759 suspended->revoked = 0;
a32da4c7 8760 suspended->suspender = strdup(str);
d76ed9a9
AS
8761 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8762 suspended->expires = str ? atoi(str) : 0;
d76ed9a9
AS
8763 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8764 suspended->reason = strdup(str ? str : "No reason");
8765 suspended->previous = NULL;
8766 cData->suspended = suspended;
8767 suspended->cData = cData;
8768 }
8769 else
a32da4c7 8770 {
8771 cData->flags &= ~CHANNEL_SUSPENDED;
d76ed9a9 8772 suspended = NULL; /* to squelch a warning */
a32da4c7 8773 }
d76ed9a9
AS
8774
8775 if(IsSuspended(cData)) {
8776 if(suspended->expires > now)
8777 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8778 else if(suspended->expires)
8779 cData->flags &= ~CHANNEL_SUSPENDED;
8780 }
8781
82f37c08 8782 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8783 {
8784 giveownership = chanserv_read_giveownership(obj);
8785 cData->giveownership = giveownership;
8786 }
8787
d76ed9a9
AS
8788 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8789 struct mod_chanmode change;
8790 mod_chanmode_init(&change);
8791 change.argc = 1;
8792 change.args[0].mode = MODE_CHANOP;
a32da4c7 8793 change.args[0].u.member = AddChannelUser(chanserv, cNode);
d76ed9a9
AS
8794 mod_chanmode_announce(chanserv, cNode, &change);
8795 }
8796
8797 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8798 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
8799 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8800 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
a32da4c7 8801 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8802 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
d76ed9a9
AS
8803 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8804 cData->max = str ? atoi(str) : 0;
8805 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8806 cData->greeting = str ? strdup(str) : NULL;
8807 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8808 cData->user_greeting = str ? strdup(str) : NULL;
8809 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8810 cData->topic_mask = str ? strdup(str) : NULL;
8811 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8812 cData->topic = str ? strdup(str) : NULL;
8813
8b9e7d45 8814 str = database_get_data(channel, KEY_MAXSETINFO, RECDB_QSTRING);
8815 cData->maxsetinfo = str ? strtoul(str, NULL, 0) : chanserv_conf.max_userinfo_length;
8816
d76ed9a9
AS
8817 if(!IsSuspended(cData)
8818 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8819 && (argc = split_line(str, 0, ArrayLength(argv), argv))
2f61d1d7 8820 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
d76ed9a9 8821 cData->modes = *modes;
1117fc5a 8822 if(off_channel > 0)
a32da4c7 8823 cData->modes.modes_set |= MODE_REGISTERED;
d76ed9a9
AS
8824 if(cData->modes.argc > 1)
8825 cData->modes.argc = 1;
8826 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8827 mod_chanmode_free(modes);
8828 }
8829
8830 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8831 for(it = dict_first(obj); it; it = iter_next(it))
8832 user_read_helper(iter_key(it), iter_data(it), cData);
8833
8834 if(!cData->users && !IsProtected(cData))
8835 {
8836 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8837 unregister_channel(cData, "has empty user list.");
8838 return 0;
8839 }
8840
8841 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8842 for(it = dict_first(obj); it; it = iter_next(it))
8843 ban_read_helper(iter_key(it), iter_data(it), cData);
8844
8845 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8846 for(it = dict_first(obj); it; it = iter_next(it))
8847 {
8848 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8849 struct record_data *rd = iter_data(it);
8850 const char *note, *setter;
8851
8852 if(rd->type != RECDB_OBJECT)
8853 {
8854 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8855 }
8856 else if(!ntype)
8857 {
8858 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8859 }
8860 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8861 {
8862 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8863 }
8864 else
8865 {
8866 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8867 if(!setter) setter = "<unknown>";
8868 chanserv_add_channel_note(cData, ntype, setter, note);
8869 }
8870 }
8871
8872 return 0;
8873}
8874
8875static void
8876chanserv_dnr_read(const char *key, struct record_data *hir)
8877{
8878 const char *setter, *reason, *str;
8879 struct do_not_register *dnr;
8880
8881 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8882 if(!setter)
8883 {
8884 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8885 return;
8886 }
8887 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8888 if(!reason)
8889 {
8890 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8891 return;
8892 }
0f6fe38c 8893 dnr = chanserv_add_dnr(key, setter, reason);
d76ed9a9
AS
8894 if(!dnr)
8895 return;
8896 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8897 if(str)
8898 dnr->set = atoi(str);
8899 else
8900 dnr->set = 0;
8901}
8902
c8ca69a0
AS
8903static void
8904chanserv_version_read(struct dict *section)
8905{
8906 /* global var.. */
8907 char *str;
8908 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
8909 if(str)
8910 chanserv_read_version = atoi(str);
8911 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
8912}
8913
d76ed9a9
AS
8914static int
8915chanserv_saxdb_read(struct dict *database)
8916{
8917 struct dict *section;
8918 dict_iterator_t it;
8919
c8ca69a0
AS
8920 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
8921 chanserv_version_read(section);
8922
d76ed9a9
AS
8923 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8924 for(it = dict_first(section); it; it = iter_next(it))
8925 chanserv_note_type_read(iter_key(it), iter_data(it));
8926
8927 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8928 for(it = dict_first(section); it; it = iter_next(it))
8929 chanserv_channel_read(iter_key(it), iter_data(it));
8930
8931 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8932 for(it = dict_first(section); it; it = iter_next(it))
8933 chanserv_dnr_read(iter_key(it), iter_data(it));
8934
8935 return 0;
8936}
8937
8938static int
8939chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8940{
8941 int high_present = 0;
8942 saxdb_start_record(ctx, KEY_USERS, 1);
8943 for(; uData; uData = uData->next)
8944 {
8945 if((uData->access >= UL_PRESENT) && uData->present)
8946 high_present = 1;
8947 saxdb_start_record(ctx, uData->handle->handle, 0);
8948 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8949 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
dfaa28a4 8950 saxdb_write_int(ctx, KEY_ACCESSEXPIRY, uData->accessexpiry);
8951 saxdb_write_int(ctx, KEY_CLVLEXPIRY, uData->clvlexpiry);
8952 saxdb_write_int(ctx, KEY_LASTLEVEL, uData->lastaccess);
d76ed9a9
AS
8953 if(uData->flags)
8954 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
b10abdb2 8955 if(uData->expires)
8956 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
d76ed9a9
AS
8957 if(uData->info)
8958 saxdb_write_string(ctx, KEY_INFO, uData->info);
8959 saxdb_end_record(ctx);
8960 }
8961 saxdb_end_record(ctx);
8962 return high_present;
8963}
8964
8965static void
8966chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8967{
8968 if(!bData)
8969 return;
8970 saxdb_start_record(ctx, KEY_BANS, 1);
8971 for(; bData; bData = bData->next)
8972 {
8973 saxdb_start_record(ctx, bData->mask, 0);
8974 saxdb_write_int(ctx, KEY_SET, bData->set);
8975 if(bData->triggered)
8976 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8977 if(bData->expires)
8978 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8979 if(bData->owner[0])
8980 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8981 if(bData->reason)
8982 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8983 saxdb_end_record(ctx);
8984 }
8985 saxdb_end_record(ctx);
8986}
8987
8988static void
8989chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8990{
8991 saxdb_start_record(ctx, name, 0);
8992 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8993 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8994 if(susp->issued)
8995 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8996 if(susp->expires)
8997 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8998 if(susp->revoked)
8999 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
9000 if(susp->previous)
9001 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
9002 saxdb_end_record(ctx);
9003}
9004
82f37c08 9005static void
9006chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
9007{
9008 saxdb_start_record(ctx, name, 0);
9009 if(giveownership->staff_issuer)
9010 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
9011 if(giveownership->old_owner)
9012 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
9013 if(giveownership->target)
9014 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
9015 if(giveownership->target_access)
9016 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
9017 if(giveownership->reason)
9018 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
9019 if(giveownership->issued)
9020 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
9021 if(giveownership->previous)
9022 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
9023 saxdb_end_record(ctx);
9024}
9025
d76ed9a9
AS
9026static void
9027chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
9028{
9029 char buf[MAXLEN];
9030 int high_present;
9031 enum levelOption lvlOpt;
9032 enum charOption chOpt;
9033
9034 saxdb_start_record(ctx, channel->channel->name, 1);
9035
9036 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
9037 saxdb_write_int(ctx, KEY_MAX, channel->max);
9038 if(channel->topic)
9039 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
9040 if(channel->registrar)
9041 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
9042 if(channel->greeting)
9043 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
9044 if(channel->user_greeting)
9045 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
9046 if(channel->topic_mask)
9047 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
9048 if(channel->suspended)
9049 chanserv_write_suspended(ctx, "suspended", channel->suspended);
82f37c08 9050 if(channel->giveownership)
9051 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
d76ed9a9
AS
9052
9053 saxdb_start_record(ctx, KEY_OPTIONS, 0);
9054 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
9055 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9056 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
9057 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9058 {
9059 buf[0] = channel->chOpts[chOpt];
9060 buf[1] = '\0';
9061 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
9062 }
9063 saxdb_end_record(ctx);
9064
8b9e7d45 9065 if (channel->maxsetinfo)
9066 saxdb_write_int(ctx, KEY_MAXSETINFO, channel->maxsetinfo);
9067
d76ed9a9
AS
9068 if(channel->modes.modes_set || channel->modes.modes_clear)
9069 {
9070 mod_chanmode_format(&channel->modes, buf);
9071 saxdb_write_string(ctx, KEY_MODES, buf);
9072 }
9073
9074 high_present = chanserv_write_users(ctx, channel->users);
9075 chanserv_write_bans(ctx, channel->bans);
9076
9077 if(dict_size(channel->notes))
9078 {
9079 dict_iterator_t it;
9080
9081 saxdb_start_record(ctx, KEY_NOTES, 1);
9082 for(it = dict_first(channel->notes); it; it = iter_next(it))
9083 {
9084 struct note *note = iter_data(it);
9085 saxdb_start_record(ctx, iter_key(it), 0);
9086 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
9087 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
9088 saxdb_end_record(ctx);
9089 }
9090 saxdb_end_record(ctx);
9091 }
9092
a32da4c7 9093 if(channel->ownerTransfer)
9094 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
d76ed9a9
AS
9095 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
9096 saxdb_end_record(ctx);
9097}
9098
9099static void
9100chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
9101{
9102 const char *str;
9103
9104 saxdb_start_record(ctx, ntype->name, 0);
9105 switch(ntype->set_access_type)
9106 {
338a82b5
AS
9107 case NOTE_SET_CHANNEL_ACCESS:
9108 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
9109 break;
9110 case NOTE_SET_CHANNEL_SETTER:
9111 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
9112 break;
9113 case NOTE_SET_PRIVILEGED: default:
9114 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
9115 break;
d76ed9a9
AS
9116 }
9117 switch(ntype->visible_type)
9118 {
338a82b5
AS
9119 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
9120 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
9121 case NOTE_VIS_PRIVILEGED:
9122 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
d76ed9a9
AS
9123 }
9124 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
9125 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
9126 saxdb_end_record(ctx);
9127}
9128
9129static void
9130write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
9131{
9132 struct do_not_register *dnr;
0f6fe38c 9133 dict_iterator_t it;
d76ed9a9 9134
0f6fe38c 9135 for(it = dict_first(dnrs); it; it = iter_next(it))
d76ed9a9
AS
9136 {
9137 dnr = iter_data(it);
9138 saxdb_start_record(ctx, dnr->chan_name, 0);
9139 if(dnr->set)
9140 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
9141 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
9142 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
9143 saxdb_end_record(ctx);
9144 }
9145}
9146
9147static int
9148chanserv_saxdb_write(struct saxdb_context *ctx)
9149{
9150 dict_iterator_t it;
9151 struct chanData *channel;
9152
c8ca69a0
AS
9153 /* Version Control*/
9154 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
9155 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
9156 saxdb_end_record(ctx);
9157
d76ed9a9
AS
9158 /* Notes */
9159 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
9160 for(it = dict_first(note_types); it; it = iter_next(it))
9161 chanserv_write_note_type(ctx, iter_data(it));
9162 saxdb_end_record(ctx);
9163
9164 /* DNRs */
9165 saxdb_start_record(ctx, KEY_DNR, 1);
9166 write_dnrs_helper(ctx, handle_dnrs);
9167 write_dnrs_helper(ctx, plain_dnrs);
9168 write_dnrs_helper(ctx, mask_dnrs);
9169 saxdb_end_record(ctx);
9170
9171 /* Channels */
9172 saxdb_start_record(ctx, KEY_CHANNELS, 1);
9173 for(channel = channelList; channel; channel = channel->next)
9174 chanserv_write_channel(ctx, channel);
9175 saxdb_end_record(ctx);
9176
9177 return 0;
9178}
9179
9180static void
9181chanserv_db_cleanup(void) {
9182 unsigned int ii;
9183 unreg_part_func(handle_part);
9184 while(channelList)
9185 unregister_channel(channelList, "terminating.");
9186 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
9187 UnlockChannel(chanserv_conf.support_channels.list[ii]);
9188 free(chanserv_conf.support_channels.list);
9189 dict_delete(handle_dnrs);
9190 dict_delete(plain_dnrs);
9191 dict_delete(mask_dnrs);
9192 dict_delete(note_types);
9193 free_string_list(chanserv_conf.eightball);
9194 free_string_list(chanserv_conf.old_ban_names);
9195 free_string_list(chanserv_conf.set_shows);
9196 free(set_shows_list.list);
9197 free(uset_shows_list.list);
9198 while(helperList)
9199 {
9200 struct userData *helper = helperList;
9201 helperList = helperList->next;
9202 free(helper);
9203 }
9204}
9205
9206#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
9207#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9208#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9209
9210void
9211init_chanserv(const char *nick)
9212{
7637f48f 9213 struct chanNode *chan;
e03ec3dc 9214 unsigned int i;
87708af4 9215
d76ed9a9
AS
9216 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
9217 conf_register_reload(chanserv_conf_read);
9218
9219 reg_server_link_func(handle_server_link);
0f6fe38c 9220
d76ed9a9
AS
9221 reg_new_channel_func(handle_new_channel);
9222 reg_join_func(handle_join);
9223 reg_part_func(handle_part);
9224 reg_kick_func(handle_kick);
9225 reg_topic_func(handle_topic);
9226 reg_mode_change_func(handle_mode);
9227 reg_nick_change_func(handle_nick_change);
ff3b058a 9228
0f6fe38c 9229 reg_auth_func(handle_auth);
d76ed9a9
AS
9230 reg_handle_rename_func(handle_rename);
9231 reg_unreg_func(handle_unreg);
9232
9233 handle_dnrs = dict_new();
9234 dict_set_free_data(handle_dnrs, free);
9235 plain_dnrs = dict_new();
9236 dict_set_free_data(plain_dnrs, free);
9237 mask_dnrs = dict_new();
9238 dict_set_free_data(mask_dnrs, free);
9239
9240 reg_svccmd_unbind_func(handle_svccmd_unbind);
9241 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
9242 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
9243 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
9244 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
9245 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
9246 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
9247 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
9248 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
9249 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
9250
ac3bdc8d
AS
9251 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
9252
d76ed9a9
AS
9253 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
9254 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
9255
4048352e
AS
9256 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9257 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9258 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9259 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9
AS
9260 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
9261
9262 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
9263 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
4048352e
AS
9264 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
9265 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9266 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
55342ce8 9267 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 9268
c9bf23fe
AS
9269 DEFINE_COMMAND(levels, 1, 0, NULL);
9270
4048352e 9271 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 9272 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
4048352e 9273 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9
AS
9274 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
9275
9276 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
9277 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
9278 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
9279 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
55342ce8 9280 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
d76ed9a9
AS
9281 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
9282 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
55342ce8 9283 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
d76ed9a9
AS
9284 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
9285 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
9286
c8273589
AS
9287 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
9288 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
9289 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
9290 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
9291 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
9292 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
d76ed9a9 9293 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
c8273589 9294 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
d76ed9a9
AS
9295 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
9296 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
4048352e 9297 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
d76ed9a9 9298 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
4048352e
AS
9299 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9300 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 9301
c8273589 9302 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
23475fc6 9303 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
c8273589
AS
9304 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
9305 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
9306
9307 /* if you change dellamer access, see also places
9308 * like unbanme which have manager hardcoded. */
9309 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
d76ed9a9
AS
9310 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
9311
c8273589
AS
9312 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
9313
d76ed9a9
AS
9314 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
9315
9316 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
9317 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9318 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9319 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9320 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9321 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9322 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
55342ce8 9323 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
d76ed9a9
AS
9324 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9325 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9326 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9327 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
9328
9329 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
9330 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
9331
9332 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
9333 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
9334 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
9335 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
9336
9337 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
9338 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
9339 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
9340 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
9341 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
9342
9343 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9344 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9345 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9346 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9347 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9348 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
b8daef0f 9349 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
33741441 9350 DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
87708af4 9351 DEFINE_COMMAND(roulette, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
9352 DEFINE_COMMAND(shoot, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
ea54b136 9353 DEFINE_COMMAND(spin, 1, MODCMD_REQUIRE_AUTHED, "spin", "+nolog,+toy,+acceptchan", NULL);
b404335b 9354
d76ed9a9
AS
9355 /* Channel options */
9356 DEFINE_CHANNEL_OPTION(defaulttopic);
9357 DEFINE_CHANNEL_OPTION(topicmask);
9358 DEFINE_CHANNEL_OPTION(greeting);
9359 DEFINE_CHANNEL_OPTION(usergreeting);
9360 DEFINE_CHANNEL_OPTION(modes);
9361 DEFINE_CHANNEL_OPTION(enfops);
55342ce8 9362 DEFINE_CHANNEL_OPTION(enfhalfops);
4b6129c0 9363 DEFINE_CHANNEL_OPTION(automode);
d76ed9a9
AS
9364 DEFINE_CHANNEL_OPTION(protect);
9365 DEFINE_CHANNEL_OPTION(enfmodes);
9366 DEFINE_CHANNEL_OPTION(enftopic);
9367 DEFINE_CHANNEL_OPTION(pubcmd);
d76ed9a9
AS
9368 DEFINE_CHANNEL_OPTION(userinfo);
9369 DEFINE_CHANNEL_OPTION(dynlimit);
9370 DEFINE_CHANNEL_OPTION(topicsnarf);
9371 DEFINE_CHANNEL_OPTION(nodelete);
9372 DEFINE_CHANNEL_OPTION(toys);
9373 DEFINE_CHANNEL_OPTION(setters);
9374 DEFINE_CHANNEL_OPTION(topicrefresh);
7637f48f 9375 DEFINE_CHANNEL_OPTION(resync);
d76ed9a9 9376 DEFINE_CHANNEL_OPTION(ctcpreaction);
31f23f13 9377 DEFINE_CHANNEL_OPTION(bantimeout);
d76ed9a9 9378 DEFINE_CHANNEL_OPTION(inviteme);
8b9e7d45 9379 DEFINE_CHANNEL_OPTION(maxsetinfo);
1117fc5a 9380 if(off_channel > 1)
d76ed9a9
AS
9381 DEFINE_CHANNEL_OPTION(offchannel);
9382 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9383
9384 /* Alias set topic to set defaulttopic for compatibility. */
9385 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9386
9387 /* User options */
d76ed9a9 9388 DEFINE_USER_OPTION(autoinvite);
cd25f2e9 9389 DEFINE_USER_OPTION(autojoin);
d76ed9a9 9390 DEFINE_USER_OPTION(info);
c8ca69a0 9391 DEFINE_USER_OPTION(autoop);
d76ed9a9
AS
9392
9393 /* Alias uset autovoice to uset autoop. */
c8ca69a0 9394 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
d76ed9a9
AS
9395
9396 note_types = dict_new();
9397 dict_set_free_data(note_types, chanserv_deref_note_type);
9398 if(nick)
9399 {
a32da4c7 9400 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
0f6fe38c 9401 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
d76ed9a9
AS
9402 service_register(chanserv)->trigger = '!';
9403 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9404 }
c8ca69a0 9405
d76ed9a9
AS
9406 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9407
9408 if(chanserv_conf.channel_expire_frequency)
9409 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9410
31f23f13
AS
9411 if(chanserv_conf.ban_timeout_frequency)
9412 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
9413
d76ed9a9
AS
9414 if(chanserv_conf.refresh_period)
9415 {
9416 time_t next_refresh;
9417 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9418 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7637f48f 9419 timeq_add(next_refresh, chanserv_auto_resync, NULL);
9420 }
9421
9422 if (autojoin_channels && chanserv) {
9423 for (i = 0; i < autojoin_channels->used; i++) {
9424 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
9425 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
9426 }
d76ed9a9 9427 }
1117fc5a 9428
d76ed9a9
AS
9429 reg_exit_func(chanserv_db_cleanup);
9430 message_register_table(msgtab);
9431}
dfaa28a4 9432