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