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