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