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