]> jfr.im git - irc/evilnet/x3.git/blame - src/chanserv.c
giveownership history support
[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;
4194
4195 cData = channel->channel_info;
4196 if(argc < 2)
4197 {
b75e24a3
AS
4198 if(cData->topic)
4199 {
4200 SetChannelTopic(channel, chanserv, cData->topic, 1);
4201 reply("CSMSG_TOPIC_SET", cData->topic);
d76ed9a9 4202 return 1;
b75e24a3 4203 }
d76ed9a9
AS
4204
4205 reply("CSMSG_NO_TOPIC", channel->name);
4206 return 0;
4207 }
4208
4209 topic = unsplit_string(argv + 1, argc - 1, NULL);
4210 /* If they say "!topic *", use an empty topic. */
4211 if((topic[0] == '*') && (topic[1] == 0))
4212 topic[0] = 0;
b75e24a3 4213
d76ed9a9
AS
4214 if(bad_topic(channel, user, topic))
4215 {
b75e24a3
AS
4216 reply("CSMSG_TOPIC_LOCKED", channel->name);
4217 return 0;
4218 }
4219 else
4220 {
4221 /* If there is a topicmask set, and the new topic doesnt match, make it */
4222 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
d76ed9a9 4223 {
b75e24a3
AS
4224 char *topic_mask = cData->topic_mask;
4225 char new_topic[TOPICLEN+1];
d76ed9a9 4226
b75e24a3
AS
4227 /* make a new topic fitting mask */
4228 conform_topic(topic_mask, topic, new_topic);
4229 if(!*new_topic)
d76ed9a9 4230 {
b75e24a3 4231 /* Topic couldnt fit into mask, was too long */
d76ed9a9
AS
4232 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4233 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4234 return 0;
4235 }
d76ed9a9 4236 SetChannelTopic(channel, chanserv, new_topic, 1);
d76ed9a9 4237 }
b75e24a3
AS
4238 else /* No mask set, just set the topic */
4239 SetChannelTopic(channel, chanserv, topic, 1);
d76ed9a9 4240 }
d76ed9a9
AS
4241
4242 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4243 {
4244 /* Grab the topic and save it as the default topic. */
4245 free(cData->topic);
4246 cData->topic = strdup(channel->topic);
4247 }
4248
4249 return 1;
4250}
4251
4252static CHANSERV_FUNC(cmd_mode)
4253{
4254 struct mod_chanmode *change;
4255
4256 if(argc < 2)
4257 {
4258 change = &channel->channel_info->modes;
4259 if(change->modes_set || change->modes_clear) {
4260 modcmd_chanmode_announce(change);
4261 reply("CSMSG_DEFAULTED_MODES", channel->name);
4262 } else
4263 reply("CSMSG_NO_MODES", channel->name);
4264 return 1;
4265 }
4266
a32da4c7 4267 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
d76ed9a9
AS
4268 if(!change)
4269 {
4270 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4271 return 0;
4272 }
4273
4274 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4275 && mode_lock_violated(&channel->channel_info->modes, change))
4276 {
4277 char modes[MAXLEN];
4278 mod_chanmode_format(&channel->channel_info->modes, modes);
4279 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4280 return 0;
4281 }
4282
4283 modcmd_chanmode_announce(change);
4284 mod_chanmode_free(change);
4285 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4286 return 1;
4287}
4288
4289static CHANSERV_FUNC(cmd_invite)
4290{
4291 struct userData *uData;
4292 struct userNode *invite;
4293
4294 uData = GetChannelUser(channel->channel_info, user->handle_info);
4295
4296 if(argc > 1)
4297 {
4298 if(!(invite = GetUserH(argv[1])))
4299 {
4300 reply("MSG_NICK_UNKNOWN", argv[1]);
4301 return 0;
4302 }
4303 }
4304 else
4305 invite = user;
4306
4307 if(GetUserMode(channel, invite))
4308 {
4309 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4310 return 0;
4311 }
4312
4313 if(user != invite)
4314 {
4315 if(argc > 2)
4316 {
4317 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4318 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4319 }
4320 else
4321 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4322 }
4323 irc_invite(chanserv, invite, channel);
4324 if(argc > 1)
4325 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4326
4327 return 1;
4328}
4329
4330static CHANSERV_FUNC(cmd_inviteme)
4331{
4332 if(GetUserMode(channel, user))
4333 {
4334 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4335 return 0;
4336 }
4337 if(channel->channel_info
4338 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4339 {
4340 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4341 return 0;
4342 }
4343 irc_invite(cmd->parent->bot, user, channel);
4344 return 1;
4345}
4346
4347static void
4348show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4349{
4350 unsigned int combo;
4351 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4352
4353 /* We display things based on two dimensions:
4354 * - Issue time: present or absent
4355 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4356 * (in order of precedence, so something both expired and revoked
4357 * only counts as revoked)
4358 */
4359 combo = (suspended->issued ? 4 : 0)
4360 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4361 switch(combo) {
4362 case 0: /* no issue time, indefinite expiration */
4363 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4364 break;
4365 case 1: /* no issue time, expires in future */
4366 intervalString(buf1, suspended->expires-now, user->handle_info);
4367 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4368 break;
4369 case 2: /* no issue time, expired */
4370 intervalString(buf1, now-suspended->expires, user->handle_info);
4371 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4372 break;
4373 case 3: /* no issue time, revoked */
4374 intervalString(buf1, now-suspended->revoked, user->handle_info);
4375 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4376 break;
4377 case 4: /* issue time set, indefinite expiration */
4378 intervalString(buf1, now-suspended->issued, user->handle_info);
4379 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4380 break;
4381 case 5: /* issue time set, expires in future */
4382 intervalString(buf1, now-suspended->issued, user->handle_info);
4383 intervalString(buf2, suspended->expires-now, user->handle_info);
4384 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4385 break;
4386 case 6: /* issue time set, expired */
4387 intervalString(buf1, now-suspended->issued, user->handle_info);
4388 intervalString(buf2, now-suspended->expires, user->handle_info);
4389 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4390 break;
4391 case 7: /* issue time set, revoked */
4392 intervalString(buf1, now-suspended->issued, user->handle_info);
4393 intervalString(buf2, now-suspended->revoked, user->handle_info);
4394 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4395 break;
4396 default:
4397 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4398 return;
4399 }
4400}
4401
82f37c08 4402static void
4403show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4404{
4405 char buf[MAXLEN];
4406 const char *fmt = "%a %b %d %H:%M %Y";
4407 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4408
4409 if(giveownership->staff_issuer)
4410 {
4411 if(giveownership->reason)
4412 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4413 giveownership->target, giveownership->target_access,
4414 giveownership->staff_issuer, buf, giveownership->reason);
4415 else
4416 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4417 giveownership->target, giveownership->target_access,
4418 giveownership->staff_issuer, buf);
4419 }
4420 else
4421 {
4422 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4423 }
4424}
4425
4426
d76ed9a9
AS
4427static CHANSERV_FUNC(cmd_info)
4428{
4429 char modes[MAXLEN], buffer[INTERVALLEN];
4430 struct userData *uData, *owner;
4431 struct chanData *cData;
4432 struct do_not_register *dnr;
4433 struct note *note;
4434 dict_iterator_t it;
4435 int privileged;
4436
4437 cData = channel->channel_info;
4438 reply("CSMSG_CHANNEL_INFO", channel->name);
de9510bc 4439 reply("CSMSG_BAR");
d76ed9a9
AS
4440
4441 uData = GetChannelUser(cData, user->handle_info);
b75e24a3 4442 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9
AS
4443 {
4444 mod_chanmode_format(&cData->modes, modes);
4445 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4446 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4447 }
4448
4449 for(it = dict_first(cData->notes); it; it = iter_next(it))
4450 {
4451 int padding;
4452
4453 note = iter_data(it);
4454 if(!note_type_visible_to_user(cData, note->type, user))
4455 continue;
4456
4457 padding = PADLEN - 1 - strlen(iter_key(it));
4458 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4459 }
4460
4461 reply("CSMSG_CHANNEL_MAX", cData->max);
4462 for(owner = cData->users; owner; owner = owner->next)
4463 if(owner->access == UL_OWNER)
4464 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4465 reply("CSMSG_CHANNEL_USERS", cData->userCount);
c8273589 4466 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
d76ed9a9 4467 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
d76ed9a9
AS
4468
4469 privileged = IsStaff(user);
697f4c9a 4470 if(privileged)
4471 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
d76ed9a9
AS
4472 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4473 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4474
4475 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4476 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4477
4478 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4479 {
4480 struct suspended *suspended;
4481 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4482 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4483 show_suspension_info(cmd, user, suspended);
4484 }
4485 else if(IsSuspended(cData))
4486 {
4487 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4488 show_suspension_info(cmd, user, cData->suspended);
4489 }
82f37c08 4490 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4491 {
4492 struct giveownership *giveownership;
4493 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4494 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4495 show_giveownership_info(cmd, user, giveownership);
4496 }
de9510bc 4497 reply("CSMSG_CHANNEL_END");
d76ed9a9
AS
4498 return 1;
4499}
4500
4501static CHANSERV_FUNC(cmd_netinfo)
4502{
4503 extern time_t boot_time;
4504 extern unsigned long burst_length;
4505 char interval[INTERVALLEN];
4506
4507 reply("CSMSG_NETWORK_INFO");
4508 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4509 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4510 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4511 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
c8273589 4512 reply("CSMSG_NETWORK_LAMERS", banCount);
d76ed9a9
AS
4513 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4514 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4515 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4516 return 1;
4517}
4518
4519static void
4520send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4521{
4522 struct helpfile_table table;
4523 unsigned int nn;
4524 struct userNode *user;
4525 char *nick;
4526
4527 table.length = 0;
4528 table.width = 1;
4529 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4530 table.contents = alloca(list->used*sizeof(*table.contents));
4531 for(nn=0; nn<list->used; nn++)
4532 {
4533 user = list->list[nn];
4534 if(user->modes & skip_flags)
4535 continue;
4536 if(IsBot(user))
4537 continue;
4538 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4539 if(IsAway(user))
4540 {
4541 nick = alloca(strlen(user->nick)+3);
4542 sprintf(nick, "(%s)", user->nick);
4543 }
4544 else
4545 nick = user->nick;
4546 table.contents[table.length][0] = nick;
4547 table.length++;
4548 }
4549 table_send(chanserv, to->nick, 0, NULL, table);
4550}
4551
4552static CHANSERV_FUNC(cmd_ircops)
4553{
4554 reply("CSMSG_STAFF_OPERS");
4555 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4556 return 1;
4557}
4558
4559static CHANSERV_FUNC(cmd_helpers)
4560{
4561 reply("CSMSG_STAFF_HELPERS");
4562 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4563 return 1;
4564}
4565
4566static CHANSERV_FUNC(cmd_staff)
4567{
4568 reply("CSMSG_NETWORK_STAFF");
4569 cmd_ircops(CSFUNC_ARGS);
4570 cmd_helpers(CSFUNC_ARGS);
4571 return 1;
4572}
4573
4574static CHANSERV_FUNC(cmd_peek)
4575{
4576 struct modeNode *mn;
4577 char modes[MODELEN];
4578 unsigned int n;
4579 struct helpfile_table table;
4580
4581 irc_make_chanmode(channel, modes);
4582
4583 reply("CSMSG_PEEK_INFO", channel->name);
de9510bc 4584 reply("CSMSG_BAR");
d76ed9a9
AS
4585 reply("CSMSG_PEEK_TOPIC", channel->topic);
4586 reply("CSMSG_PEEK_MODES", modes);
4587 reply("CSMSG_PEEK_USERS", channel->members.used);
4588
4589 table.length = 0;
4590 table.width = 1;
4591 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4592 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4593 for(n = 0; n < channel->members.used; n++)
4594 {
4595 mn = channel->members.list[n];
4596 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4597 continue;
4598 table.contents[table.length] = alloca(sizeof(**table.contents));
4599 table.contents[table.length][0] = mn->user->nick;
4600 table.length++;
4601 }
4602 if(table.length)
4603 {
4604 reply("CSMSG_PEEK_OPS");
4605 table_send(chanserv, user->nick, 0, NULL, table);
4606 }
4607 else
4608 reply("CSMSG_PEEK_NO_OPS");
de9510bc 4609 reply("CSMSG_PEEK_END");
d76ed9a9
AS
4610 return 1;
4611}
4612
4613static MODCMD_FUNC(cmd_wipeinfo)
4614{
4615 struct handle_info *victim;
4616 struct userData *ud, *actor;
4617
4618 REQUIRE_PARAMS(2);
4619 actor = GetChannelUser(channel->channel_info, user->handle_info);
4620 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4621 return 0;
4622 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4623 {
4624 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4625 return 0;
4626 }
4627 if((ud->access >= actor->access) && (ud != actor))
4628 {
4629 reply("MSG_USER_OUTRANKED", victim->handle);
4630 return 0;
4631 }
4632 if(ud->info)
4633 free(ud->info);
4634 ud->info = NULL;
4635 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4636 return 1;
4637}
4638
4639static CHANSERV_FUNC(cmd_resync)
4640{
4641 struct mod_chanmode *changes;
4642 struct chanData *cData = channel->channel_info;
4643 unsigned int ii, used;
4644
e3b2f789
AS
4645 /* 6 = worst case -ovh+ovh on everyone */
4646 changes = mod_chanmode_alloc(channel->members.used * 6);
d76ed9a9
AS
4647 for(ii = used = 0; ii < channel->members.used; ++ii)
4648 {
4649 struct modeNode *mn = channel->members.list[ii];
4650 struct userData *uData;
4651
4652 if(IsService(mn->user))
4653 continue;
4654
e3b2f789 4655
d76ed9a9 4656 uData = GetChannelAccess(cData, mn->user->handle_info);
e3b2f789
AS
4657
4658 /* If the channel is in no-mode mode, de-mode EVERYONE */
4659 if(cData->chOpts[chAutomode] == 'n')
55342ce8 4660 {
e3b2f789
AS
4661 if(mn->modes)
4662 {
4663 changes->args[used].mode = MODE_REMOVE | mn->modes;
4664 changes->args[used++].u.member = mn;
4665 }
55342ce8 4666 }
e3b2f789 4667 else /* Give various userlevels their modes.. */
d76ed9a9 4668 {
e3b2f789 4669 if(uData && uData->access >= UL_OP )
d76ed9a9 4670 {
e3b2f789
AS
4671 if(!(mn->modes & MODE_CHANOP))
4672 {
4673 changes->args[used].mode = MODE_CHANOP;
4674 changes->args[used++].u.member = mn;
4675 }
d76ed9a9 4676 }
e3b2f789 4677 else if(uData && uData->access >= UL_HALFOP)
55342ce8 4678 {
e3b2f789
AS
4679 if(mn->modes & MODE_CHANOP)
4680 {
4681 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4682 changes->args[used++].u.member = mn;
4683 }
4684 if(!(mn->modes & MODE_HALFOP))
4685 {
4686 changes->args[used].mode = MODE_HALFOP;
4687 changes->args[used++].u.member = mn;
4688 }
55342ce8 4689 }
e3b2f789 4690 else if(uData && uData->access >= UL_PEON )
d76ed9a9 4691 {
e3b2f789
AS
4692 if(mn->modes & MODE_CHANOP)
4693 {
4694 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4695 changes->args[used++].u.member = mn;
4696 }
4697 if(mn->modes & MODE_HALFOP)
4698 {
4699 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
4700 changes->args[used++].u.member = mn;
4701 }
4702 /* Don't voice peons if were in mode m */
4703 if( cData->chOpts[chAutomode] == 'm')
4704 {
4705 if(mn->modes & MODE_VOICE)
4706 {
4707 changes->args[used].mode = MODE_REMOVE | MODE_VOICE;
4708 changes->args[used++].u.member = mn;
4709 }
4710 }
4711 /* otherwise, make user they do have voice */
4712 else if(!(mn->modes & MODE_VOICE))
4713 {
4714 changes->args[used].mode = MODE_VOICE;
4715 changes->args[used++].u.member = mn;
4716 }
d76ed9a9 4717 }
e3b2f789 4718 else /* They arnt on the userlist.. */
d76ed9a9 4719 {
e3b2f789
AS
4720 /* If we voice everyone, but they dont.. */
4721 if(cData->chOpts[chAutomode] == 'v')
4722 {
4723 /* Remove anything except v */
4724 if(mn->modes & ~MODE_VOICE)
4725 {
4726 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4727 changes->args[used++].u.member = mn;
4728 }
4729 /* Add v */
4730 if(!(mn->modes & MODE_VOICE))
4731 {
4732 changes->args[used].mode = MODE_VOICE;
4733 changes->args[used++].u.member = mn;
4734 }
4735 }
4736 /* If we hop everyone, but they dont.. */
4737 else if(cData->chOpts[chAutomode] == 'h')
4738 {
4739 /* Remove anything except h */
4740 if(mn->modes & ~MODE_HALFOP)
4741 {
4742 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP);
4743 changes->args[used++].u.member = mn;
4744 }
4745 /* Add h */
4746 if(!(mn->modes & MODE_HALFOP))
4747 {
4748 changes->args[used].mode = MODE_HALFOP;
4749 changes->args[used++].u.member = mn;
4750 }
4751 }
4752 /* If we op everyone, but they dont.. */
4753 else if(cData->chOpts[chAutomode] == 'o')
4754 {
4755 /* Remove anything except h */
4756 if(mn->modes & ~MODE_CHANOP)
4757 {
4758 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP);
4759 changes->args[used++].u.member = mn;
4760 }
4761 /* Add h */
4762 if(!(mn->modes & MODE_CHANOP))
4763 {
4764 changes->args[used].mode = MODE_CHANOP;
4765 changes->args[used++].u.member = mn;
4766 }
4767 }
4768 /* they have no excuse for having modes, de-everything them */
4769 else
4770 {
4771 if(mn->modes)
4772 {
4773 changes->args[used].mode = MODE_REMOVE | mn->modes;
4774 changes->args[used++].u.member = mn;
4775 }
4776 }
d76ed9a9
AS
4777 }
4778 }
4779 }
4780 changes->argc = used;
4781 modcmd_chanmode_announce(changes);
4782 mod_chanmode_free(changes);
4783 reply("CSMSG_RESYNCED_USERS", channel->name);
4784 return 1;
4785}
4786
4787static CHANSERV_FUNC(cmd_seen)
4788{
4789 struct userData *uData;
4790 struct handle_info *handle;
4791 char seen[INTERVALLEN];
4792
4793 REQUIRE_PARAMS(2);
4794
4795 if(!irccasecmp(argv[1], chanserv->nick))
4796 {
4797 reply("CSMSG_IS_CHANSERV");
4798 return 1;
4799 }
4800
4801 if(!(handle = get_handle_info(argv[1])))
4802 {
4803 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4804 return 0;
4805 }
4806
4807 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4808 {
4809 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4810 return 0;
4811 }
4812
4813 if(uData->present)
4814 reply("CSMSG_USER_PRESENT", handle->handle);
4815 else if(uData->seen)
4816 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4817 else
4818 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4819
4820 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4821 reply("CSMSG_USER_VACATION", handle->handle);
4822
4823 return 1;
4824}
4825
4826static MODCMD_FUNC(cmd_names)
4827{
4828 struct userNode *targ;
4829 struct userData *targData;
4830 unsigned int ii, pos;
4831 char buf[400];
4832
4833 for(ii=pos=0; ii<channel->members.used; ++ii)
4834 {
4835 targ = channel->members.list[ii]->user;
4836 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4837 if(!targData)
4838 continue;
4839 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4840 {
4841 buf[pos] = 0;
4842 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4843 pos = 0;
4844 }
4845 buf[pos++] = ' ';
4846 if(IsUserSuspended(targData))
4847 buf[pos++] = 's';
4848 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4849 }
4850 buf[pos] = 0;
4851 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4852 reply("CSMSG_END_NAMES", channel->name);
4853 return 1;
4854}
4855
4856static int
4857note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4858{
4859 switch(ntype->visible_type)
4860 {
4861 case NOTE_VIS_ALL: return 1;
4862 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4863 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4864 }
4865}
4866
4867static int
4868note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4869{
4870 struct userData *uData;
4871
4872 switch(ntype->set_access_type)
4873 {
4874 case NOTE_SET_CHANNEL_ACCESS:
4875 if(!user->handle_info)
4876 return 0;
4877 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4878 return 0;
4879 return uData->access >= ntype->set_access.min_ulevel;
4880 case NOTE_SET_CHANNEL_SETTER:
4881 return check_user_level(channel, user, lvlSetters, 1, 0);
4882 case NOTE_SET_PRIVILEGED: default:
4883 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4884 }
4885}
4886
4887static CHANSERV_FUNC(cmd_note)
4888{
4889 struct chanData *cData;
4890 struct note *note;
4891 struct note_type *ntype;
4892
4893 cData = channel->channel_info;
4894 if(!cData)
4895 {
4896 reply("CSMSG_NOT_REGISTERED", channel->name);
4897 return 0;
4898 }
4899
4900 /* If no arguments, show all visible notes for the channel. */
4901 if(argc < 2)
4902 {
4903 dict_iterator_t it;
4904 unsigned int count;
4905
4906 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4907 {
4908 note = iter_data(it);
4909 if(!note_type_visible_to_user(cData, note->type, user))
4910 continue;
4911 if(!count++)
4912 reply("CSMSG_NOTELIST_HEADER", channel->name);
4913 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4914 }
4915 if(count)
4916 reply("CSMSG_NOTELIST_END", channel->name);
4917 else
4918 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4919 }
4920 /* If one argument, show the named note. */
4921 else if(argc == 2)
4922 {
4923 if((note = dict_find(cData->notes, argv[1], NULL))
4924 && note_type_visible_to_user(cData, note->type, user))
4925 {
4926 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4927 }
4928 else if((ntype = dict_find(note_types, argv[1], NULL))
4929 && note_type_visible_to_user(NULL, ntype, user))
4930 {
4931 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4932 return 0;
4933 }
4934 else
4935 {
4936 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4937 return 0;
4938 }
4939 }
4940 /* Assume they're trying to set a note. */
4941 else
4942 {
4943 char *note_text;
4944 ntype = dict_find(note_types, argv[1], NULL);
4945 if(!ntype)
4946 {
4947 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4948 return 0;
4949 }
4950 else if(note_type_settable_by_user(channel, ntype, user))
4951 {
4952 note_text = unsplit_string(argv+2, argc-2, NULL);
4953 if((note = dict_find(cData->notes, argv[1], NULL)))
4954 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4955 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4956 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4957
4958 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4959 {
4960 /* The note is viewable to staff only, so return 0
4961 to keep the invocation from getting logged (or
4962 regular users can see it in !events). */
4963 return 0;
4964 }
4965 }
4966 else
4967 {
4968 reply("CSMSG_NO_ACCESS");
4969 return 0;
4970 }
4971 }
4972 return 1;
4973}
4974
4975static CHANSERV_FUNC(cmd_delnote)
4976{
4977 struct note *note;
4978
4979 REQUIRE_PARAMS(2);
4980 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4981 || !note_type_settable_by_user(channel, note->type, user))
4982 {
4983 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4984 return 0;
4985 }
4986 dict_remove(channel->channel_info->notes, note->type->name);
4987 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4988 return 1;
4989}
4990
23475fc6 4991static CHANSERV_FUNC(cmd_last)
4992{
4993 int numoflines;
4994
4995 REQUIRE_PARAMS(1);
4996
4997 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
4998
4999 if(numoflines < 1 || numoflines > 200)
5000 {
5001 reply("CSMSG_LAST_INVALID");
5002 return 0;
5003 }
5004 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
5005 return 1;
5006}
5007
d76ed9a9
AS
5008static CHANSERV_FUNC(cmd_events)
5009{
5010 struct logSearch discrim;
5011 struct logReport report;
5012 unsigned int matches, limit;
5013
5014 limit = (argc > 1) ? atoi(argv[1]) : 10;
5015 if(limit < 1 || limit > 200)
5016 limit = 10;
5017
5018 memset(&discrim, 0, sizeof(discrim));
5019 discrim.masks.bot = chanserv;
5020 discrim.masks.channel_name = channel->name;
5021 if(argc > 2)
5022 discrim.masks.command = argv[2];
5023 discrim.limit = limit;
5024 discrim.max_time = INT_MAX;
5025 discrim.severities = 1 << LOG_COMMAND;
5026 report.reporter = chanserv;
5027 report.user = user;
de9510bc
AS
5028 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
5029 reply("CSMSG_BAR");
d76ed9a9
AS
5030 matches = log_entry_search(&discrim, log_report_entry, &report);
5031 if(matches)
5032 reply("MSG_MATCH_COUNT", matches);
5033 else
5034 reply("MSG_NO_MATCHES");
5035 return 1;
5036}
5037
5038static CHANSERV_FUNC(cmd_say)
5039{
5040 char *msg;
5041 if(channel)
5042 {
5043 REQUIRE_PARAMS(2);
5044 msg = unsplit_string(argv + 1, argc - 1, NULL);
5045 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5046 }
5047 else if(GetUserH(argv[1]))
5048 {
5049 REQUIRE_PARAMS(3);
5050 msg = unsplit_string(argv + 2, argc - 2, NULL);
5051 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5052 }
5053 else
5054 {
5055 reply("MSG_NOT_TARGET_NAME");
5056 return 0;
5057 }
5058 return 1;
5059}
5060
5061static CHANSERV_FUNC(cmd_emote)
5062{
5063 char *msg;
5064 assert(argc >= 2);
5065 if(channel)
5066 {
5067 /* CTCP is so annoying. */
5068 msg = unsplit_string(argv + 1, argc - 1, NULL);
5069 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5070 }
5071 else if(GetUserH(argv[1]))
5072 {
5073 msg = unsplit_string(argv + 2, argc - 2, NULL);
5074 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5075 }
5076 else
5077 {
5078 reply("MSG_NOT_TARGET_NAME");
5079 return 0;
5080 }
5081 return 1;
5082}
5083
5084struct channelList *
5085chanserv_support_channels(void)
5086{
5087 return &chanserv_conf.support_channels;
5088}
5089
5090static CHANSERV_FUNC(cmd_expire)
5091{
5092 int channel_count = registered_channels;
5093 expire_channels(NULL);
5094 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5095 return 1;
5096}
5097
5098static void
5099chanserv_expire_suspension(void *data)
5100{
5101 struct suspended *suspended = data;
5102 struct chanNode *channel;
d76ed9a9
AS
5103
5104 if(!suspended->expires || (now < suspended->expires))
5105 suspended->revoked = now;
5106 channel = suspended->cData->channel;
5107 suspended->cData->channel = channel;
5108 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
0d16e639 5109 if(!IsOffChannel(suspended->cData))
5110 {
5111 struct mod_chanmode change;
5112 mod_chanmode_init(&change);
5113 change.argc = 1;
5114 change.args[0].mode = MODE_CHANOP;
5115 change.args[0].u.member = AddChannelUser(chanserv, channel);
5116 mod_chanmode_announce(chanserv, channel, &change);
5117 }
d76ed9a9
AS
5118}
5119
5120static CHANSERV_FUNC(cmd_csuspend)
5121{
5122 struct suspended *suspended;
5123 char reason[MAXLEN];
5124 time_t expiry, duration;
5125 struct userData *uData;
5126
5127 REQUIRE_PARAMS(3);
5128
5129 if(IsProtected(channel->channel_info))
5130 {
5131 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5132 return 0;
5133 }
5134
5135 if(argv[1][0] == '!')
5136 argv[1]++;
5137 else if(IsSuspended(channel->channel_info))
5138 {
5139 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5140 show_suspension_info(cmd, user, channel->channel_info->suspended);
5141 return 0;
5142 }
5143
5144 if(!strcmp(argv[1], "0"))
5145 expiry = 0;
5146 else if((duration = ParseInterval(argv[1])))
5147 expiry = now + duration;
5148 else
5149 {
5150 reply("MSG_INVALID_DURATION", argv[1]);
5151 return 0;
5152 }
5153
5154 unsplit_string(argv + 2, argc - 2, reason);
5155
5156 suspended = calloc(1, sizeof(*suspended));
5157 suspended->revoked = 0;
5158 suspended->issued = now;
5159 suspended->suspender = strdup(user->handle_info->handle);
5160 suspended->expires = expiry;
5161 suspended->reason = strdup(reason);
5162 suspended->cData = channel->channel_info;
5163 suspended->previous = suspended->cData->suspended;
5164 suspended->cData->suspended = suspended;
5165
5166 if(suspended->expires)
5167 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5168
5169 if(IsSuspended(channel->channel_info))
5170 {
5171 suspended->previous->revoked = now;
5172 if(suspended->previous->expires)
5173 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5174 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5175 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5176 }
5177 else
5178 {
5179 /* Mark all users in channel as absent. */
5180 for(uData = channel->channel_info->users; uData; uData = uData->next)
5181 {
5182 if(uData->present)
5183 {
5184 uData->seen = now;
5185 uData->present = 0;
5186 }
5187 }
5188
5189 /* Mark the channel as suspended, then part. */
5190 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5191 DelChannelUser(chanserv, channel, suspended->reason, 0);
5192 reply("CSMSG_SUSPENDED", channel->name);
5193 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5194 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5195 }
5196 return 1;
5197}
5198
5199static CHANSERV_FUNC(cmd_cunsuspend)
5200{
5201 struct suspended *suspended;
5202 char message[MAXLEN];
5203
5204 if(!IsSuspended(channel->channel_info))
5205 {
5206 reply("CSMSG_NOT_SUSPENDED", channel->name);
5207 return 0;
5208 }
5209
5210 suspended = channel->channel_info->suspended;
5211
5212 /* Expire the suspension and join ChanServ to the channel. */
5213 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5214 chanserv_expire_suspension(suspended);
5215 reply("CSMSG_UNSUSPENDED", channel->name);
5216 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5217 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5218 return 1;
5219}
5220
5221typedef struct chanservSearch
5222{
5223 char *name;
5224 char *registrar;
5225
5226 time_t unvisited;
5227 time_t registered;
5228
5229 unsigned long flags;
5230 unsigned int limit;
5231} *search_t;
5232
5233typedef void (*channel_search_func)(struct chanData *channel, void *data);
5234
5235static search_t
5236chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5237{
5238 search_t search;
5239 unsigned int i;
5240
5241 search = malloc(sizeof(struct chanservSearch));
5242 memset(search, 0, sizeof(*search));
5243 search->limit = 25;
5244
5245 for(i = 0; i < argc; i++)
5246 {
5247 /* Assume all criteria require arguments. */
5248 if(i == (argc - 1))
5249 {
5250 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5251 goto fail;
5252 }
5253
5254 if(!irccasecmp(argv[i], "name"))
5255 search->name = argv[++i];
5256 else if(!irccasecmp(argv[i], "registrar"))
5257 search->registrar = argv[++i];
5258 else if(!irccasecmp(argv[i], "unvisited"))
5259 search->unvisited = ParseInterval(argv[++i]);
5260 else if(!irccasecmp(argv[i], "registered"))
5261 search->registered = ParseInterval(argv[++i]);
5262 else if(!irccasecmp(argv[i], "flags"))
5263 {
5264 i++;
5265 if(!irccasecmp(argv[i], "nodelete"))
5266 search->flags |= CHANNEL_NODELETE;
5267 else if(!irccasecmp(argv[i], "suspended"))
5268 search->flags |= CHANNEL_SUSPENDED;
5269 else
5270 {
5271 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5272 goto fail;
5273 }
5274 }
5275 else if(!irccasecmp(argv[i], "limit"))
5276 search->limit = strtoul(argv[++i], NULL, 10);
5277 else
5278 {
5279 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5280 goto fail;
5281 }
5282 }
5283
5284 if(search->name && !strcmp(search->name, "*"))
5285 search->name = 0;
5286 if(search->registrar && !strcmp(search->registrar, "*"))
5287 search->registrar = 0;
5288
5289 return search;
5290 fail:
5291 free(search);
5292 return NULL;
5293}
5294
5295static int
5296chanserv_channel_match(struct chanData *channel, search_t search)
5297{
5298 const char *name = channel->channel->name;
5299 if((search->name && !match_ircglob(name, search->name)) ||
5300 (search->registrar && !channel->registrar) ||
5301 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5302 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5303 (search->registered && (now - channel->registered) > search->registered) ||
5304 (search->flags && ((search->flags & channel->flags) != search->flags)))
5305 return 0;
5306
5307 return 1;
5308}
5309
5310static unsigned int
5311chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5312{
5313 struct chanData *channel;
5314 unsigned int matches = 0;
5315
5316 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5317 {
5318 if(!chanserv_channel_match(channel, search))
5319 continue;
5320 matches++;
5321 smf(channel, data);
5322 }
5323
5324 return matches;
5325}
5326
5327static void
5328search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5329{
5330}
5331
5332static void
5333search_print(struct chanData *channel, void *data)
5334{
5335 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5336}
5337
5338static CHANSERV_FUNC(cmd_search)
5339{
5340 search_t search;
5341 unsigned int matches;
5342 channel_search_func action;
5343
5344 REQUIRE_PARAMS(3);
5345
5346 if(!irccasecmp(argv[1], "count"))
5347 action = search_count;
5348 else if(!irccasecmp(argv[1], "print"))
5349 action = search_print;
5350 else
5351 {
5352 reply("CSMSG_ACTION_INVALID", argv[1]);
5353 return 0;
5354 }
5355
5356 search = chanserv_search_create(user, argc - 2, argv + 2);
5357 if(!search)
5358 return 0;
5359
5360 if(action == search_count)
5361 search->limit = INT_MAX;
5362
5363 if(action == search_print)
de9510bc 5364 {
d76ed9a9 5365 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
de9510bc
AS
5366 reply("CSMSG_BAR");
5367 }
d76ed9a9
AS
5368
5369 matches = chanserv_channel_search(search, action, user);
5370
5371 if(matches)
5372 reply("MSG_MATCH_COUNT", matches);
5373 else
5374 reply("MSG_NO_MATCHES");
5375
5376 free(search);
5377 return 1;
5378}
5379
5380static CHANSERV_FUNC(cmd_unvisited)
5381{
5382 struct chanData *cData;
5383 time_t interval = chanserv_conf.channel_expire_delay;
5384 char buffer[INTERVALLEN];
5385 unsigned int limit = 25, matches = 0;
5386
5387 if(argc > 1)
5388 {
5389 interval = ParseInterval(argv[1]);
5390 if(argc > 2)
5391 limit = atoi(argv[2]);
5392 }
5393
5394 intervalString(buffer, interval, user->handle_info);
5395 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5396
5397 for(cData = channelList; cData && matches < limit; cData = cData->next)
5398 {
5399 if((now - cData->visited) < interval)
5400 continue;
5401
5402 intervalString(buffer, now - cData->visited, user->handle_info);
5403 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5404 matches++;
5405 }
5406
5407 return 1;
5408}
5409
5410static MODCMD_FUNC(chan_opt_defaulttopic)
5411{
5412 if(argc > 1)
5413 {
5414 char *topic;
5415
5416 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5417 {
5418 reply("CSMSG_TOPIC_LOCKED", channel->name);
5419 return 0;
5420 }
5421
5422 topic = unsplit_string(argv+1, argc-1, NULL);
5423
5424 free(channel->channel_info->topic);
5425 if(topic[0] == '*' && topic[1] == 0)
5426 {
5427 topic = channel->channel_info->topic = NULL;
5428 }
5429 else
5430 {
5431 topic = channel->channel_info->topic = strdup(topic);
5432 if(channel->channel_info->topic_mask
5433 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5434 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5435 }
5436 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5437 }
5438
5439 if(channel->channel_info->topic)
5440 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5441 else
5442 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5443 return 1;
5444}
5445
5446static MODCMD_FUNC(chan_opt_topicmask)
5447{
5448 if(argc > 1)
5449 {
5450 struct chanData *cData = channel->channel_info;
5451 char *mask;
5452
5453 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5454 {
5455 reply("CSMSG_TOPIC_LOCKED", channel->name);
5456 return 0;
5457 }
5458
5459 mask = unsplit_string(argv+1, argc-1, NULL);
5460
5461 if(cData->topic_mask)
5462 free(cData->topic_mask);
5463 if(mask[0] == '*' && mask[1] == 0)
5464 {
5465 cData->topic_mask = 0;
5466 }
5467 else
5468 {
5469 cData->topic_mask = strdup(mask);
5470 if(!cData->topic)
5471 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5472 else if(!match_ircglob(cData->topic, cData->topic_mask))
5473 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5474 }
5475 }
5476
5477 if(channel->channel_info->topic_mask)
5478 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5479 else
5480 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5481 return 1;
5482}
5483
5484int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5485{
5486 if(argc > 1)
5487 {
5488 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5489 char *previous;
5490
5491 previous = *data;
5492 if(greeting[0] == '*' && greeting[1] == 0)
5493 *data = NULL;
5494 else
5495 {
5496 unsigned int length = strlen(greeting);
5497 if(length > chanserv_conf.greeting_length)
5498 {
5499 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5500 return 0;
5501 }
5502 *data = strdup(greeting);
5503 }
5504 if(previous)
5505 free(previous);
5506 }
5507
5508 if(*data)
5509 reply(name, *data);
5510 else
5511 reply(name, user_find_message(user, "MSG_NONE"));
5512 return 1;
5513}
5514
5515static MODCMD_FUNC(chan_opt_greeting)
5516{
5517 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5518}
5519
5520static MODCMD_FUNC(chan_opt_usergreeting)
5521{
5522 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5523}
5524
5525static MODCMD_FUNC(chan_opt_modes)
5526{
5527 struct mod_chanmode *new_modes;
5528 char modes[MODELEN];
5529
5530 if(argc > 1)
5531 {
5532 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5533 {
5534 reply("CSMSG_NO_ACCESS");
5535 return 0;
5536 }
5537 if(argv[1][0] == '*' && argv[1][1] == 0)
5538 {
5539 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5540 }
a32da4c7 5541 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
d76ed9a9
AS
5542 {
5543 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5544 return 0;
5545 }
5546 else if(new_modes->argc > 1)
5547 {
5548 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5549 mod_chanmode_free(new_modes);
5550 return 0;
5551 }
5552 else
5553 {
5554 channel->channel_info->modes = *new_modes;
5555 modcmd_chanmode_announce(new_modes);
5556 mod_chanmode_free(new_modes);
5557 }
5558 }
5559
5560 mod_chanmode_format(&channel->channel_info->modes, modes);
5561 if(modes[0])
5562 reply("CSMSG_SET_MODES", modes);
5563 else
5564 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5565 return 1;
5566}
5567
5568#define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5569static int
5570channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5571{
5572 struct chanData *cData = channel->channel_info;
5573 int value;
5574
5575 if(argc > 1)
5576 {
5577 /* Set flag according to value. */
5578 if(enabled_string(argv[1]))
5579 {
5580 cData->flags |= mask;
5581 value = 1;
5582 }
5583 else if(disabled_string(argv[1]))
5584 {
5585 cData->flags &= ~mask;
5586 value = 0;
5587 }
5588 else
5589 {
5590 reply("MSG_INVALID_BINARY", argv[1]);
5591 return 0;
5592 }
5593 }
5594 else
5595 {
5596 /* Find current option value. */
5597 value = (cData->flags & mask) ? 1 : 0;
5598 }
5599
5600 if(value)
5601 reply(name, user_find_message(user, "MSG_ON"));
5602 else
5603 reply(name, user_find_message(user, "MSG_OFF"));
5604 return 1;
5605}
5606
5607static MODCMD_FUNC(chan_opt_nodelete)
5608{
5609 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5610 {
5611 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5612 return 0;
5613 }
5614
5615 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5616}
5617
5618static MODCMD_FUNC(chan_opt_dynlimit)
5619{
5620 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5621}
5622
5623static MODCMD_FUNC(chan_opt_offchannel)
5624{
5625 struct chanData *cData = channel->channel_info;
5626 int value;
5627
5628 if(argc > 1)
5629 {
5630 /* Set flag according to value. */
5631 if(enabled_string(argv[1]))
5632 {
5633 if(!IsOffChannel(cData))
5634 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5635 cData->flags |= CHANNEL_OFFCHANNEL;
5636 value = 1;
5637 }
5638 else if(disabled_string(argv[1]))
5639 {
5640 if(IsOffChannel(cData))
5641 {
5642 struct mod_chanmode change;
5643 mod_chanmode_init(&change);
5644 change.argc = 1;
5645 change.args[0].mode = MODE_CHANOP;
a32da4c7 5646 change.args[0].u.member = AddChannelUser(chanserv, channel);
d76ed9a9
AS
5647 mod_chanmode_announce(chanserv, channel, &change);
5648 }
5649 cData->flags &= ~CHANNEL_OFFCHANNEL;
5650 value = 0;
5651 }
5652 else
5653 {
5654 reply("MSG_INVALID_BINARY", argv[1]);
5655 return 0;
5656 }
5657 }
5658 else
5659 {
5660 /* Find current option value. */
5661 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5662 }
5663
5664 if(value)
5665 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5666 else
5667 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5668 return 1;
5669}
5670
5671static MODCMD_FUNC(chan_opt_defaults)
5672{
5673 struct userData *uData;
5674 struct chanData *cData;
5675 const char *confirm;
5676 enum levelOption lvlOpt;
5677 enum charOption chOpt;
5678
5679 cData = channel->channel_info;
5680 uData = GetChannelUser(cData, user->handle_info);
5681 if(!uData || (uData->access < UL_OWNER))
5682 {
5683 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5684 return 0;
5685 }
5686 confirm = make_confirmation_string(uData);
5687 if((argc < 2) || strcmp(argv[1], confirm))
5688 {
5689 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5690 return 0;
5691 }
5692 cData->flags = CHANNEL_DEFAULT_FLAGS;
5693 cData->modes = chanserv_conf.default_modes;
5694 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5695 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5696 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5697 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5698 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5699 return 1;
5700}
5701
5702static int
5703channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5704{
5705 struct chanData *cData = channel->channel_info;
5706 struct userData *uData;
5707 unsigned short value;
5708
5709 if(argc > 1)
5710 {
5711 if(!check_user_level(channel, user, option, 1, 1))
5712 {
5713 reply("CSMSG_CANNOT_SET");
5714 return 0;
5715 }
5716 value = user_level_from_name(argv[1], UL_OWNER+1);
5717 if(!value && strcmp(argv[1], "0"))
5718 {
5719 reply("CSMSG_INVALID_ACCESS", argv[1]);
5720 return 0;
5721 }
5722 uData = GetChannelUser(cData, user->handle_info);
5723 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5724 {
5725 reply("CSMSG_BAD_SETLEVEL");
5726 return 0;
5727 }
5728 switch(option)
5729 {
4b6129c0
AS
5730 case lvlSetters:
5731 /* This test only applies to owners, since non-owners
5732 * trying to set an option to above their level get caught
5733 * by the CSMSG_BAD_SETLEVEL test above.
5734 */
5735 if(value > uData->access)
5736 {
5737 reply("CSMSG_BAD_SETTERS");
5738 return 0;
5739 }
5740 break;
5741 default:
5742 break;
d76ed9a9
AS
5743 }
5744 cData->lvlOpts[option] = value;
5745 }
5746 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5747 return argc > 1;
5748}
5749
5750static MODCMD_FUNC(chan_opt_enfops)
5751{
5752 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5753}
5754
55342ce8 5755static MODCMD_FUNC(chan_opt_enfhalfops)
5756{
5757 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
5758}
d76ed9a9
AS
5759static MODCMD_FUNC(chan_opt_enfmodes)
5760{
5761 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5762}
5763
5764static MODCMD_FUNC(chan_opt_enftopic)
5765{
5766 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5767}
5768
5769static MODCMD_FUNC(chan_opt_pubcmd)
5770{
5771 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5772}
5773
5774static MODCMD_FUNC(chan_opt_setters)
5775{
5776 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5777}
5778
d76ed9a9
AS
5779static MODCMD_FUNC(chan_opt_userinfo)
5780{
5781 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5782}
5783
d76ed9a9
AS
5784static MODCMD_FUNC(chan_opt_topicsnarf)
5785{
5786 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5787}
5788
5789static MODCMD_FUNC(chan_opt_inviteme)
5790{
5791 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5792}
5793
31f23f13
AS
5794/* TODO: Make look like this when no args are
5795 * given:
5796 * -X3- -------------------------------
5797 * -X3- BanTimeout: Bans are removed:
5798 * -X3- ----- * indicates current -----
5799 * -X3- 0: [*] Never.
5800 * -X3- 1: [ ] After 10 minutes.
5801 * -X3- 2: [ ] After 2 hours.
5802 * -X3- 3: [ ] After 4 hours.
5803 * -X3- 4: [ ] After 24 hours.
5804 * -X3- 5: [ ] After one week.
5805 * -X3- ------------- End -------------
5806 */
d76ed9a9
AS
5807static int
5808channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5809{
5810 struct chanData *cData = channel->channel_info;
5811 int count = charOptions[option].count, index;
5812
5813 if(argc > 1)
5814 {
5815 index = atoi(argv[1]);
5816
5817 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5818 {
5819 reply("CSMSG_INVALID_NUMERIC", index);
5820 /* Show possible values. */
5821 for(index = 0; index < count; index++)
5822 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5823 return 0;
5824 }
5825
5826 cData->chOpts[option] = charOptions[option].values[index].value;
5827 }
5828 else
5829 {
5830 /* Find current option value. */
5831 find_value:
5832 for(index = 0;
5833 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5834 index++);
5835 if(index == count)
5836 {
5837 /* Somehow, the option value is corrupt; reset it to the default. */
5838 cData->chOpts[option] = charOptions[option].default_value;
5839 goto find_value;
5840 }
5841 }
5842
5843 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5844 return 1;
5845}
5846
4b6129c0 5847static MODCMD_FUNC(chan_opt_automode)
c8ca69a0 5848{
4b6129c0 5849 return channel_multiple_option(chAutomode, CSFUNC_ARGS);
c8ca69a0
AS
5850}
5851
d76ed9a9
AS
5852static MODCMD_FUNC(chan_opt_protect)
5853{
5854 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5855}
5856
5857static MODCMD_FUNC(chan_opt_toys)
5858{
5859 return channel_multiple_option(chToys, CSFUNC_ARGS);
5860}
5861
5862static MODCMD_FUNC(chan_opt_ctcpreaction)
5863{
5864 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5865}
5866
31f23f13
AS
5867static MODCMD_FUNC(chan_opt_bantimeout)
5868{
5869 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
5870}
5871
d76ed9a9
AS
5872static MODCMD_FUNC(chan_opt_topicrefresh)
5873{
5874 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5875}
5876
5877static struct svccmd_list set_shows_list;
5878
5879static void
5880handle_svccmd_unbind(struct svccmd *target) {
5881 unsigned int ii;
5882 for(ii=0; ii<set_shows_list.used; ++ii)
5883 if(target == set_shows_list.list[ii])
5884 set_shows_list.used = 0;
5885}
5886
5887static CHANSERV_FUNC(cmd_set)
5888{
5889 struct svccmd *subcmd;
5890 char buf[MAXLEN];
5891 unsigned int ii;
5892
5893 /* Check if we need to (re-)initialize set_shows_list. */
5894 if(!set_shows_list.used)
5895 {
5896 if(!set_shows_list.size)
5897 {
5898 set_shows_list.size = chanserv_conf.set_shows->used;
5899 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5900 }
5901 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5902 {
5903 const char *name = chanserv_conf.set_shows->list[ii];
5904 sprintf(buf, "%s %s", argv[0], name);
5905 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5906 if(!subcmd)
5907 {
5908 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5909 continue;
5910 }
5911 svccmd_list_append(&set_shows_list, subcmd);
5912 }
5913 }
5914
5915 if(argc < 2)
5916 {
de9510bc
AS
5917 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
5918 reply("CSMSG_BAR");
d76ed9a9
AS
5919 for(ii = 0; ii < set_shows_list.used; ii++)
5920 {
5921 subcmd = set_shows_list.list[ii];
5922 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5923 }
de9510bc 5924 reply("CSMSG_CHANNEL_OPTIONS_END");
d76ed9a9
AS
5925 return 1;
5926 }
5927
5928 sprintf(buf, "%s %s", argv[0], argv[1]);
5929 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5930 if(!subcmd)
5931 {
5932 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5933 return 0;
5934 }
5935 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5936 {
5937 reply("CSMSG_NO_ACCESS");
5938 return 0;
5939 }
5940
5941 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5942}
5943
5944static int
5945user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5946{
5947 struct userData *uData;
5948
5949 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5950 if(!uData)
5951 {
5952 reply("CSMSG_NOT_USER", channel->name);
5953 return 0;
5954 }
5955
5956 if(argc < 2)
5957 {
5958 /* Just show current option value. */
5959 }
5960 else if(enabled_string(argv[1]))
5961 {
5962 uData->flags |= mask;
5963 }
5964 else if(disabled_string(argv[1]))
5965 {
5966 uData->flags &= ~mask;
5967 }
5968 else
5969 {
5970 reply("MSG_INVALID_BINARY", argv[1]);
5971 return 0;
5972 }
5973
5974 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5975 return 1;
5976}
5977
c8ca69a0 5978static MODCMD_FUNC(user_opt_autoop)
d76ed9a9
AS
5979{
5980 struct userData *uData;
5981
5982 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5983 if(!uData)
5984 {
5985 reply("CSMSG_NOT_USER", channel->name);
5986 return 0;
5987 }
b75e24a3 5988 if(uData->access < UL_OP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
c8ca69a0 5989 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
d76ed9a9 5990 else
c8ca69a0 5991 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
b75e24a3 5992 /* TODO: add halfops error message? or is the op one generic enough? */
d76ed9a9
AS
5993}
5994
5995static MODCMD_FUNC(user_opt_autoinvite)
5996{
5997 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5998}
5999
6000static MODCMD_FUNC(user_opt_info)
6001{
6002 struct userData *uData;
6003 char *infoline;
6004
6005 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6006
6007 if(!uData)
6008 {
6009 /* If they got past the command restrictions (which require access)
6010 * but fail this test, we have some fool with security override on.
6011 */
6012 reply("CSMSG_NOT_USER", channel->name);
6013 return 0;
6014 }
6015
6016 if(argc > 1)
6017 {
6018 size_t bp;
6019 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6020 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6021 {
6022 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6023 return 0;
6024 }
6025 bp = strcspn(infoline, "\001");
6026 if(infoline[bp])
6027 {
6028 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6029 return 0;
6030 }
6031 if(uData->info)
6032 free(uData->info);
6033 if(infoline[0] == '*' && infoline[1] == 0)
6034 uData->info = NULL;
6035 else
6036 uData->info = strdup(infoline);
6037 }
6038 if(uData->info)
6039 reply("CSMSG_USET_INFO", uData->info);
6040 else
6041 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6042 return 1;
6043}
6044
6045struct svccmd_list uset_shows_list;
6046
6047static CHANSERV_FUNC(cmd_uset)
6048{
6049 struct svccmd *subcmd;
6050 char buf[MAXLEN];
6051 unsigned int ii;
6052
6053 /* Check if we need to (re-)initialize uset_shows_list. */
6054 if(!uset_shows_list.used)
6055 {
6056 char *options[] =
6057 {
c8ca69a0 6058 "AutoOp", "AutoInvite", "Info"
d76ed9a9
AS
6059 };
6060
6061 if(!uset_shows_list.size)
6062 {
6063 uset_shows_list.size = ArrayLength(options);
6064 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6065 }
6066 for(ii = 0; ii < ArrayLength(options); ii++)
6067 {
6068 const char *name = options[ii];
6069 sprintf(buf, "%s %s", argv[0], name);
6070 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6071 if(!subcmd)
6072 {
6073 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6074 continue;
6075 }
6076 svccmd_list_append(&uset_shows_list, subcmd);
6077 }
6078 }
6079
6080 if(argc < 2)
6081 {
6082 /* Do this so options are presented in a consistent order. */
6083 reply("CSMSG_USER_OPTIONS");
6084 for(ii = 0; ii < uset_shows_list.used; ii++)
6085 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6086 return 1;
6087 }
6088
6089 sprintf(buf, "%s %s", argv[0], argv[1]);
6090 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6091 if(!subcmd)
6092 {
6093 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6094 return 0;
6095 }
6096
6097 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6098}
6099
6100static CHANSERV_FUNC(cmd_giveownership)
6101{
6102 struct handle_info *new_owner_hi;
6103 struct userData *new_owner, *curr_user;
6104 struct chanData *cData = channel->channel_info;
6105 struct do_not_register *dnr;
82f37c08 6106 struct giveownership *giveownership;
6107 unsigned int force, override;
6108 unsigned short co_access, new_owner_old_access;
6109 char reason[MAXLEN], transfer_reason[MAXLEN];
d76ed9a9
AS
6110
6111 REQUIRE_PARAMS(2);
6112 curr_user = GetChannelAccess(cData, user->handle_info);
6113 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
82f37c08 6114
6115 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6116 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6117 && (uData->access > 500)
6118 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6119 || uData->access < 500));
6120
6121
d76ed9a9
AS
6122 if(!curr_user || (curr_user->access != UL_OWNER))
6123 {
6124 struct userData *owner = NULL;
6125 for(curr_user = channel->channel_info->users;
6126 curr_user;
6127 curr_user = curr_user->next)
6128 {
6129 if(curr_user->access != UL_OWNER)
6130 continue;
6131 if(owner)
6132 {
6133 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6134 return 0;
6135 }
6136 owner = curr_user;
6137 }
6138 curr_user = owner;
6139 }
a32da4c7 6140 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
6141 {
6142 char delay[INTERVALLEN];
6143 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6144 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6145 return 0;
6146 }
d76ed9a9
AS
6147 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6148 return 0;
6149 if(new_owner_hi == user->handle_info)
6150 {
6151 reply("CSMSG_NO_TRANSFER_SELF");
6152 return 0;
6153 }
6154 new_owner = GetChannelAccess(cData, new_owner_hi);
6155 if(!new_owner)
6156 {
0d16e639 6157 if(force)
6158 {
6159 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
6160 }
6161 else
6162 {
6163 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6164 return 0;
6165 }
d76ed9a9
AS
6166 }
6167 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6168 {
6169 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6170 return 0;
6171 }
6172 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6173 if(!IsHelping(user))
6174 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6175 else
1117fc5a 6176 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
d76ed9a9
AS
6177 return 0;
6178 }
82f37c08 6179
6180 new_owner_old_access = new_owner->access;
d76ed9a9
AS
6181 if(new_owner->access >= UL_COOWNER)
6182 co_access = new_owner->access;
6183 else
6184 co_access = UL_COOWNER;
6185 new_owner->access = UL_OWNER;
6186 if(curr_user)
6187 curr_user->access = co_access;
a32da4c7 6188 cData->ownerTransfer = now;
82f37c08 6189
6190 giveownership = calloc(1, sizeof(*giveownership));
6191 giveownership->issued = now;
6192 giveownership->old_owner = curr_user->handle->handle;
6193 giveownership->target = new_owner_hi->handle;
6194 giveownership->target_access = new_owner_old_access;
6195 if(override)
6196 {
6197 if(argc > (2 + force))
6198 {
6199 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6200 giveownership->reason = strdup(transfer_reason);
6201 }
6202 giveownership->staff_issuer = strdup(user->handle_info->handle);
6203 }
6204
6205 giveownership->previous = channel->channel_info->giveownership;
6206 channel->channel_info->giveownership = giveownership;
6207
d76ed9a9
AS
6208 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6209 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6210 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6211 return 1;
6212}
6213
b10abdb2 6214static void
6215chanserv_expire_user_suspension(void *data)
6216{
6217 struct userData *target = data;
6218
6219 target->expires = 0;
6220 target->flags &= ~USER_SUSPENDED;
6221}
6222
d76ed9a9
AS
6223static CHANSERV_FUNC(cmd_suspend)
6224{
6225 struct handle_info *hi;
6226 struct userData *self, *target;
b10abdb2 6227 time_t expiry;
d76ed9a9 6228
b10abdb2 6229 REQUIRE_PARAMS(3);
d76ed9a9
AS
6230 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6231 self = GetChannelUser(channel->channel_info, user->handle_info);
6232 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6233 {
6234 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6235 return 0;
6236 }
6237 if(target->access >= self->access)
6238 {
6239 reply("MSG_USER_OUTRANKED", hi->handle);
6240 return 0;
6241 }
6242 if(target->flags & USER_SUSPENDED)
6243 {
6244 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6245 return 0;
6246 }
6247 if(target->present)
6248 {
6249 target->present = 0;
6250 target->seen = now;
6251 }
b10abdb2 6252 if(!strcmp(argv[2], "0"))
6253 expiry = 0;
6254 else
6255 {
6256 unsigned int duration;
6257 if(!(duration = ParseInterval(argv[2])))
6258 {
6259 reply("MSG_INVALID_DURATION", argv[2]);
6260 return 0;
6261 }
6262 expiry = now + duration;
6263 }
6264
6265 target->expires = expiry;
6266
6267 if(target->expires)
6268 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6269
d76ed9a9
AS
6270 target->flags |= USER_SUSPENDED;
6271 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6272 return 1;
6273}
6274
6275static CHANSERV_FUNC(cmd_unsuspend)
6276{
6277 struct handle_info *hi;
6278 struct userData *self, *target;
6279
6280 REQUIRE_PARAMS(2);
6281 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6282 self = GetChannelUser(channel->channel_info, user->handle_info);
6283 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6284 {
6285 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6286 return 0;
6287 }
6288 if(target->access >= self->access)
6289 {
6290 reply("MSG_USER_OUTRANKED", hi->handle);
6291 return 0;
6292 }
6293 if(!(target->flags & USER_SUSPENDED))
6294 {
6295 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6296 return 0;
6297 }
6298 target->flags &= ~USER_SUSPENDED;
a32da4c7 6299 scan_user_presence(target, NULL);
b10abdb2 6300 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
d76ed9a9
AS
6301 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6302 return 1;
6303}
6304
6305static MODCMD_FUNC(cmd_deleteme)
6306{
6307 struct handle_info *hi;
6308 struct userData *target;
6309 const char *confirm_string;
6310 unsigned short access;
6311 char *channel_name;
6312
6313 hi = user->handle_info;
6314 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6315 {
6316 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6317 return 0;
6318 }
6319 if(target->access == UL_OWNER)
6320 {
6321 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6322 return 0;
6323 }
6324 confirm_string = make_confirmation_string(target);
6325 if((argc < 2) || strcmp(argv[1], confirm_string))
6326 {
6327 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6328 return 0;
6329 }
6330 access = target->access;
6331 channel_name = strdup(channel->name);
6332 del_channel_user(target, 1);
6333 reply("CSMSG_DELETED_YOU", access, channel_name);
6334 free(channel_name);
6335 return 1;
6336}
6337
6338static void
6339chanserv_refresh_topics(UNUSED_ARG(void *data))
6340{
6341 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6342 struct chanData *cData;
6343 char opt;
6344
6345 for(cData = channelList; cData; cData = cData->next)
6346 {
6347 if(IsSuspended(cData))
6348 continue;
6349 opt = cData->chOpts[chTopicRefresh];
6350 if(opt == 'n')
6351 continue;
6352 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6353 continue;
6354 if(cData->topic)
6355 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6356 cData->last_refresh = refresh_num;
6357 }
6358 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6359}
6360
6361static CHANSERV_FUNC(cmd_unf)
6362{
6363 if(channel)
6364 {
6365 char response[MAXLEN];
6366 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6367 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6368 irc_privmsg(cmd->parent->bot, channel->name, response);
6369 }
6370 else
6371 reply("CSMSG_UNF_RESPONSE");
6372 return 1;
6373}
6374
6375static CHANSERV_FUNC(cmd_ping)
6376{
6377 if(channel)
6378 {
6379 char response[MAXLEN];
6380 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6381 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6382 irc_privmsg(cmd->parent->bot, channel->name, response);
6383 }
6384 else
6385 reply("CSMSG_PING_RESPONSE");
6386 return 1;
6387}
6388
6389static CHANSERV_FUNC(cmd_wut)
6390{
6391 if(channel)
6392 {
6393 char response[MAXLEN];
6394 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6395 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6396 irc_privmsg(cmd->parent->bot, channel->name, response);
6397 }
6398 else
6399 reply("CSMSG_WUT_RESPONSE");
6400 return 1;
6401}
6402
240a3274 6403#ifdef lame8ball
d76ed9a9
AS
6404static CHANSERV_FUNC(cmd_8ball)
6405{
6406 unsigned int i, j, accum;
6407 const char *resp;
6408
6409 REQUIRE_PARAMS(2);
6410 accum = 0;
6411 for(i=1; i<argc; i++)
6412 for(j=0; argv[i][j]; j++)
6413 accum = (accum << 5) - accum + toupper(argv[i][j]);
6414 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6415 if(channel)
6416 {
6417 char response[MAXLEN];
6418 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6419 irc_privmsg(cmd->parent->bot, channel->name, response);
6420 }
6421 else
6422 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6423 return 1;
6424}
6425
240a3274
AS
6426#else /* Use cool 8ball instead */
6427
6428void eightball(char *outcome, int method, unsigned int seed)
6429{
6430 int answer = 0;
6431
6432#define NUMOFCOLORS 18
6433 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
6434 "white", "black", "grey", "brown",
6435 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6436 "fuchsia","turquoise","magenta", "cyan"};
6437#define NUMOFLOCATIONS 50
6438 char balllocations[50][55] = {
6439 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6440 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6441 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6442 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6443 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6444 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6445 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6446 "your bra", "your hair", "your bed", "the couch", "the wall",
6447 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6448 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6449#define NUMOFPREPS 15
6450 char ballpreps[50][50] = {
6451 "Near", "Somewhere near", "In", "In", "In",
6452 "In", "Hiding in", "Under", "Next to", "Over",
6453 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6454#define NUMOFNUMS 34
6455 char ballnums[50][50] = {
6456 "A hundred", "A thousand", "A few", "42",
6457 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6458 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6459 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6460 };
6461#define NUMOFMULTS 8
6462 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6463
6464 /* Method:
6465 * 0: normal (Not used in x3)
6466 * 1: color
6467 * 2: where is
6468 * 3: how many
6469 */
6470
6471 srand(seed);
6472 if (method == 1) /* A Color */
6473 {
6474 char tmp[MAXLEN];
6475
6476 answer = (rand() % 12); /* Make sure this is the # of entries */
6477 switch(answer)
6478 {
6479 case 0: strcpy(tmp, "Very bright %s, I'd say.");
6480 break;
6481 case 1: strcpy(tmp, "Sort of a light %s color.");
6482 break;
6483 case 2: strcpy(tmp, "Dark and dreary %s.");
6484 break;
6485 case 3: strcpy(tmp, "Quite a pale shade of %s.");
6486 break;
6487 case 4: strcpy(tmp, "A gross kind of mucky %s.");
6488 break;
6489 case 5: strcpy(tmp, "Brilliant whiteish %s.");
6490 break;
6491 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
6492 break;
6493 case 10: strcpy(tmp, "Solid %s.");
6494 break;
6495 case 11: strcpy(tmp, "Transparent %s.");
6496 break;
6497 default: strcpy(outcome, "An invalid random number was generated.");
6498 return;
6499 }
6500 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
6501 return;
6502 }
6503 else if (method == 2) /* Location */
6504 {
6505 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
6506 }
6507 else if (method == 3) /* Number of ___ */
6508 {
6509 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
6510 }
6511 else
6512 {
6513 //Debug(DBGWARNING, "Error in 8ball.");
6514 }
6515 return;
6516}
6517
6518static CHANSERV_FUNC(cmd_8ball)
6519{
6520 char *word1, *word2, *word3;
6521 static char eb[MAXLEN];
6522 unsigned int accum, i, j;
6523
132f7859 6524 REQUIRE_PARAMS(2);
240a3274
AS
6525 accum = 0;
6526 for(i=1; i<argc; i++)
6527 for(j=0; argv[i][j]; j++)
6528 accum = (accum << 5) - accum + toupper(argv[i][j]);
6529
6530 accum += time(NULL)/3600;
6531 word1 = argv[1];
6532 word2 = argc>2?argv[2]:"";
6533 word3 = argc>3?argv[3]:"";
6534
6535/*** COLOR *****/
6536 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
6537 eightball(eb, 1, accum);
6538 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6539 eightball(eb, 1, accum);
6540 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6541 eightball(eb, 1, accum);
6542/*** LOCATION *****/
6543 else if(
6544 (
6545 word2 &&
6546 (
6547 (strcasecmp(word1, "where") == 0) &&
6548 (strcasecmp(word2, "is") == 0)
6549 )
6550 ) ||
6551 (
6552 strcasecmp(word1, "where's") == 0
6553 )
6554 )
6555 eightball(eb, 2, accum);
6556/*** NUMBER *****/
6557 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
6558 eightball(eb, 3, accum);
6559/*** GENERIC *****/
6560 else
6561 {
6562 /* Generic 8ball question.. so pull from x3.conf srvx style */
6563 const char *resp;
6564
6565 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6566 if(channel)
6567 {
6568 char response[MAXLEN];
6569 sprintf(response, "\002%s\002: %s", user->nick, resp);
6570 irc_privmsg(cmd->parent->bot, channel->name, response);
6571 }
6572 else
6573 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6574 return 1;
6575 }
6576
6577 if(channel)
6578 {
6579 char response[MAXLEN];
6580 sprintf(response, "\002%s\002: %s", user->nick, eb);
6581 irc_privmsg(cmd->parent->bot, channel->name, response);
6582 }
6583 else
6584 send_message_type(4, user, cmd->parent->bot, "%s", eb);
6585 return 1;
6586}
6587#endif
6588
d76ed9a9
AS
6589static CHANSERV_FUNC(cmd_d)
6590{
6591 unsigned long sides, count, modifier, ii, total;
6592 char response[MAXLEN], *sep;
6593 const char *fmt;
6594
6595 REQUIRE_PARAMS(2);
6596 if((count = strtoul(argv[1], &sep, 10)) < 1)
6597 goto no_dice;
6598 if(sep[0] == 0)
6599 {
6600 if(count == 1)
6601 goto no_dice;
6602 sides = count;
6603 count = 1;
6604 modifier = 0;
6605 }
6606 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6607 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6608 {
6609 if(sep[0] == 0)
6610 modifier = 0;
6611 else if((sep[0] == '-') && isdigit(sep[1]))
6612 modifier = strtoul(sep, NULL, 10);
6613 else if((sep[0] == '+') && isdigit(sep[1]))
6614 modifier = strtoul(sep+1, NULL, 10);
6615 else
6616 goto no_dice;
6617 }
6618 else
6619 {
6620 no_dice:
6621 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6622 return 0;
6623 }
6624 if(count > 10)
6625 {
6626 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6627 return 0;
6628 }
6629 for(total = ii = 0; ii < count; ++ii)
6630 total += (rand() % sides) + 1;
6631 total += modifier;
6632
6633 if((count > 1) || modifier)
6634 {
6635 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6636 sprintf(response, fmt, total, count, sides, modifier);
6637 }
6638 else
6639 {
6640 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6641 sprintf(response, fmt, total, sides);
6642 }
6643 if(channel)
6644 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6645 else
6646 send_message_type(4, user, cmd->parent->bot, "%s", response);
6647 return 1;
6648}
6649
6650static CHANSERV_FUNC(cmd_huggle)
6651{
6652 /* CTCP must be via PRIVMSG, never notice */
6653 if(channel)
6654 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6655 else
6656 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6657 return 1;
6658}
6659
e5a8f7cd 6660static CHANSERV_FUNC(cmd_calc)
6661{
6662 char response[MAXLEN];
6663
6664 REQUIRE_PARAMS(2);
6665 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
6666
6667 if(channel)
d8cf9c21 6668 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
e5a8f7cd 6669 else
6670 send_message_type(4, user, cmd->parent->bot, "%s", response);
6671 return 1;
6672}
6673
d76ed9a9
AS
6674static void
6675chanserv_adjust_limit(void *data)
6676{
6677 struct mod_chanmode change;
6678 struct chanData *cData = data;
6679 struct chanNode *channel = cData->channel;
6680 unsigned int limit;
6681
6682 if(IsSuspended(cData))
6683 return;
6684
6685 cData->limitAdjusted = now;
6686 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6687 if(cData->modes.modes_set & MODE_LIMIT)
6688 {
6689 if(limit > cData->modes.new_limit)
6690 limit = cData->modes.new_limit;
6691 else if(limit == cData->modes.new_limit)
6692 return;
6693 }
6694
6695 mod_chanmode_init(&change);
6696 change.modes_set = MODE_LIMIT;
6697 change.new_limit = limit;
6698 mod_chanmode_announce(chanserv, channel, &change);
6699}
6700
6701static void
6702handle_new_channel(struct chanNode *channel)
6703{
6704 struct chanData *cData;
6705
6706 if(!(cData = channel->channel_info))
6707 return;
6708
6709 if(cData->modes.modes_set || cData->modes.modes_clear)
6710 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6711
6712 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6713 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6714}
6715
6716/* Welcome to my worst nightmare. Warning: Read (or modify)
6717 the code below at your own risk. */
6718static int
6719handle_join(struct modeNode *mNode)
6720{
6721 struct mod_chanmode change;
6722 struct userNode *user = mNode->user;
6723 struct chanNode *channel = mNode->channel;
6724 struct chanData *cData;
6725 struct userData *uData = NULL;
6726 struct banData *bData;
6727 struct handle_info *handle;
6728 unsigned int modes = 0, info = 0;
6729 char *greeting;
6730
6731 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6732 return 0;
6733
6734 cData = channel->channel_info;
6735 if(channel->members.used > cData->max)
6736 cData->max = channel->members.used;
6737
d76ed9a9
AS
6738 mod_chanmode_init(&change);
6739 change.argc = 1;
6740 if(channel->banlist.used < MAXBANS)
6741 {
6742 /* Not joining through a ban. */
6743 for(bData = cData->bans;
6744 bData && !user_matches_glob(user, bData->mask, 1);
6745 bData = bData->next);
6746
6747 if(bData)
6748 {
6749 char kick_reason[MAXLEN];
6750 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6751
6752 bData->triggered = now;
6753 if(bData != cData->bans)
6754 {
6755 /* Shuffle the ban to the head of the list. */
6756 if(bData->next)
6757 bData->next->prev = bData->prev;
6758 if(bData->prev)
6759 bData->prev->next = bData->next;
6760
6761 bData->prev = NULL;
6762 bData->next = cData->bans;
6763
6764 if(cData->bans)
6765 cData->bans->prev = bData;
6766 cData->bans = bData;
6767 }
6768
6769 change.args[0].mode = MODE_BAN;
a32da4c7 6770 change.args[0].u.hostmask = bData->mask;
d76ed9a9
AS
6771 mod_chanmode_announce(chanserv, channel, &change);
6772 KickChannelUser(user, channel, chanserv, kick_reason);
6773 return 1;
6774 }
6775 }
6776
6777 /* ChanServ will not modify the limits in join-flooded channels.
6778 It will also skip DynLimit processing when the user (or srvx)
6779 is bursting in, because there are likely more incoming. */
6780 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6781 && !user->uplink->burst
6782 && !channel->join_flooded
6783 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6784 {
6785 /* The user count has begun "bumping" into the channel limit,
6786 so set a timer to raise the limit a bit. Any previous
6787 timers are removed so three incoming users within the delay
6788 results in one limit change, not three. */
6789
6790 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6791 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6792 }
6793
4b6129c0
AS
6794 /* Give automodes exept during join-floods */
6795 if(!channel->join_flooded)
d76ed9a9 6796 {
4b6129c0
AS
6797 if(cData->chOpts[chAutomode] == 'v')
6798 modes |= MODE_VOICE;
6799 else if(cData->chOpts[chAutomode] == 'h')
6800 modes |= MODE_HALFOP;
6801 else if(cData->chOpts[chAutomode] == 'o')
6802 modes |= MODE_CHANOP;
d76ed9a9 6803 }
c8ca69a0 6804
d76ed9a9
AS
6805 greeting = cData->greeting;
6806 if(user->handle_info)
6807 {
6808 handle = user->handle_info;
6809
6810 if(IsHelper(user) && !IsHelping(user))
6811 {
6812 unsigned int ii;
6813 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6814 {
6815 if(channel == chanserv_conf.support_channels.list[ii])
6816 {
6817 HANDLE_SET_FLAG(user->handle_info, HELPING);
6818 break;
6819 }
6820 }
6821 }
6822
6823 uData = GetTrueChannelAccess(cData, handle);
6824 if(uData && !IsUserSuspended(uData))
6825 {
4b6129c0
AS
6826 /* non users getting automodes are handled above. */
6827 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 6828 {
4b6129c0 6829 if(uData->access >= UL_OP )
d76ed9a9 6830 modes |= MODE_CHANOP;
4b6129c0 6831 else if(uData->access >= UL_HALFOP )
55342ce8 6832 modes |= MODE_HALFOP;
4b6129c0 6833 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
d76ed9a9
AS
6834 modes |= MODE_VOICE;
6835 }
6836 if(uData->access >= UL_PRESENT)
6837 cData->visited = now;
6838 if(cData->user_greeting)
6839 greeting = cData->user_greeting;
6840 if(uData->info
6841 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6842 && ((now - uData->seen) >= chanserv_conf.info_delay)
6843 && !uData->present)
6844 info = 1;
6845 uData->seen = now;
6846 uData->present = 1;
6847 }
6848 }
6849 if(!user->uplink->burst)
6850 {
6851 if(modes)
6852 {
4b6129c0 6853 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
55342ce8 6854 if(modes & MODE_CHANOP) {
6855 modes &= ~MODE_HALFOP;
d76ed9a9 6856 modes &= ~MODE_VOICE;
55342ce8 6857 }
4b6129c0 6858 */
d76ed9a9 6859 change.args[0].mode = modes;
a32da4c7 6860 change.args[0].u.member = mNode;
d76ed9a9
AS
6861 mod_chanmode_announce(chanserv, channel, &change);
6862 }
6863 if(greeting && !user->uplink->burst)
6864 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6865 if(uData && info)
6866 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6867 }
6868 return 0;
6869}
6870
6871static void
6872handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6873{
6874 struct mod_chanmode change;
6875 struct userData *channel;
6876 unsigned int ii, jj;
6877
6878 if(!user->handle_info)
6879 return;
6880
6881 mod_chanmode_init(&change);
6882 change.argc = 1;
6883 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6884 {
6885 struct chanNode *cn;
6886 struct modeNode *mn;
6887 if(IsUserSuspended(channel)
6888 || IsSuspended(channel->channel)
6889 || !(cn = channel->channel->channel))
6890 continue;
6891
6892 mn = GetUserMode(cn, user);
6893 if(!mn)
6894 {
6895 if(!IsUserSuspended(channel)
6896 && IsUserAutoInvite(channel)
6897 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6898 && !self->burst
6899 && !user->uplink->burst)
6900 irc_invite(chanserv, user, cn);
6901 continue;
6902 }
6903
6904 if(channel->access >= UL_PRESENT)
6905 channel->channel->visited = now;
6906
6907 if(IsUserAutoOp(channel))
6908 {
4b6129c0 6909 if(channel->access >= UL_OP )
d76ed9a9 6910 change.args[0].mode = MODE_CHANOP;
4b6129c0 6911 else if(channel->access >= UL_HALFOP )
55342ce8 6912 change.args[0].mode = MODE_HALFOP;
4b6129c0 6913 else if(channel->access >= UL_PEON )
d76ed9a9
AS
6914 change.args[0].mode = MODE_VOICE;
6915 else
6916 change.args[0].mode = 0;
a32da4c7 6917 change.args[0].u.member = mn;
d76ed9a9
AS
6918 if(change.args[0].mode)
6919 mod_chanmode_announce(chanserv, cn, &change);
6920 }
6921
6922 channel->seen = now;
6923 channel->present = 1;
6924 }
6925
6926 for(ii = 0; ii < user->channels.used; ++ii)
6927 {
6928 struct chanNode *channel = user->channels.list[ii]->channel;
6929 struct banData *ban;
6930
55342ce8 6931 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
1117fc5a 6932 || !channel->channel_info
6933 || IsSuspended(channel->channel_info))
d76ed9a9
AS
6934 continue;
6935 for(jj = 0; jj < channel->banlist.used; ++jj)
6936 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6937 break;
6938 if(jj < channel->banlist.used)
6939 continue;
6940 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6941 {
6942 char kick_reason[MAXLEN];
6943 if(!user_matches_glob(user, ban->mask, 1))
6944 continue;
6945 change.args[0].mode = MODE_BAN;
a32da4c7 6946 change.args[0].u.hostmask = ban->mask;
d76ed9a9
AS
6947 mod_chanmode_announce(chanserv, channel, &change);
6948 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6949 KickChannelUser(user, channel, chanserv, kick_reason);
6950 ban->triggered = now;
6951 break;
6952 }
6953 }
6954
6955 if(IsSupportHelper(user))
6956 {
6957 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6958 {
6959 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6960 {
6961 HANDLE_SET_FLAG(user->handle_info, HELPING);
6962 break;
6963 }
6964 }
6965 }
6966}
6967
6968static void
6969handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6970{
6971 struct chanData *cData;
6972 struct userData *uData;
6973
6974 cData = mn->channel->channel_info;
6975 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6976 return;
6977
6978 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6979 {
6980 /* Allow for a bit of padding so that the limit doesn't
6981 track the user count exactly, which could get annoying. */
6982 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6983 {
6984 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6985 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6986 }
6987 }
6988
6989 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6990 {
6991 scan_user_presence(uData, mn->user);
6992 uData->seen = now;
6993 }
6994
6995 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6996 {
6997 unsigned int ii, jj;
6998 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6999 {
7000 for(jj = 0; jj < mn->user->channels.used; ++jj)
7001 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
7002 break;
7003 if(jj < mn->user->channels.used)
7004 break;
7005 }
7006 if(ii == chanserv_conf.support_channels.used)
7007 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7008 }
7009}
7010
7011static void
7012handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7013{
7014 struct userData *uData;
7015
7016 if(!channel->channel_info || !kicker || IsService(kicker)
7017 || (kicker == victim) || IsSuspended(channel->channel_info)
7018 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7019 return;
7020
7021 if(protect_user(victim, kicker, channel->channel_info))
7022 {
31f23f13 7023 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
d76ed9a9
AS
7024 KickChannelUser(kicker, channel, chanserv, reason);
7025 }
7026
7027 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7028 uData->seen = now;
7029}
7030
7031static int
7032handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7033{
7034 struct chanData *cData;
7035
7036 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7037 return 0;
7038
7039 cData = channel->channel_info;
7040 if(bad_topic(channel, user, channel->topic))
b75e24a3 7041 { /* User doesnt have privs to set topics. Undo it */
d76ed9a9 7042 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
b75e24a3 7043 SetChannelTopic(channel, chanserv, old_topic, 1);
d76ed9a9
AS
7044 return 1;
7045 }
b75e24a3
AS
7046 /* If there is a topic mask set, and the new topic doesnt match,
7047 * set the topic to mask + new_topic */
7048 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
7049 {
7050 char new_topic[TOPICLEN+1];
7051 conform_topic(cData->topic_mask, channel->topic, new_topic);
7052 if(*new_topic)
7053 {
7054 SetChannelTopic(channel, chanserv, new_topic, 1);
7055 /* and fall through to topicsnarf code below.. */
7056 }
7057 else /* Topic couldnt fit into mask, was too long */
7058 {
7059 SetChannelTopic(channel, chanserv, old_topic, 1);
7060 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
7061 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
7062 return 1;
7063 }
7064 }
d76ed9a9
AS
7065 /* With topicsnarf, grab the topic and save it as the default topic. */
7066 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7067 {
7068 free(cData->topic);
7069 cData->topic = strdup(channel->topic);
7070 }
7071 return 0;
7072}
7073
7074static void
7075handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7076{
7077 struct mod_chanmode *bounce = NULL;
7078 unsigned int bnc, ii;
7079 char deopped = 0;
7080
7081 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7082 return;
7083
7084 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7085 && mode_lock_violated(&channel->channel_info->modes, change))
7086 {
7087 char correct[MAXLEN];
7088 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7089 mod_chanmode_format(&channel->channel_info->modes, correct);
7090 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7091 }
7092 for(ii = bnc = 0; ii < change->argc; ++ii)
7093 {
7094 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7095 {
a32da4c7 7096 const struct userNode *victim = change->args[ii].u.member->user;
d76ed9a9
AS
7097 if(!protect_user(victim, user, channel->channel_info))
7098 continue;
7099 if(!bounce)
7100 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7101 if(!deopped)
7102 {
7103 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 7104 bounce->args[bnc].u.member = GetUserMode(channel, user);
7105 if(bounce->args[bnc].u.member)
d76ed9a9
AS
7106 bnc++;
7107 deopped = 1;
7108 }
7109 bounce->args[bnc].mode = MODE_CHANOP;
a32da4c7 7110 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9
AS
7111 bnc++;
7112 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7113 }
7114 else if(change->args[ii].mode & MODE_CHANOP)
7115 {
a32da4c7 7116 const struct userNode *victim = change->args[ii].u.member->user;
d76ed9a9
AS
7117 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7118 continue;
7119 if(!bounce)
7120 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7121 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 7122 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9
AS
7123 bnc++;
7124 }
7125 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7126 {
a32da4c7 7127 const char *ban = change->args[ii].u.hostmask;
d76ed9a9
AS
7128 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7129 continue;
7130 if(!bounce)
7131 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7132 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 7133 bounce->args[bnc].u.hostmask = strdup(ban);
d76ed9a9
AS
7134 bnc++;
7135 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7136 }
7137 }
7138 if(bounce)
7139 {
7140 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7141 mod_chanmode_announce(chanserv, channel, bounce);
ec1a68c8 7142 for(ii = 0; ii < change->argc; ++ii)
7143 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7144 free((char*)bounce->args[ii].u.hostmask);
d76ed9a9
AS
7145 mod_chanmode_free(bounce);
7146 }
7147}
7148
7149static void
7150handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7151{
7152 struct chanNode *channel;
7153 struct banData *bData;
7154 struct mod_chanmode change;
7155 unsigned int ii, jj;
7156 char kick_reason[MAXLEN];
7157
7158 mod_chanmode_init(&change);
7159 change.argc = 1;
7160 change.args[0].mode = MODE_BAN;
7161 for(ii = 0; ii < user->channels.used; ++ii)
7162 {
7163 channel = user->channels.list[ii]->channel;
7164 /* Need not check for bans if they're opped or voiced. */
4b6129c0
AS
7165 /* TODO: does this make sense in automode v, h, and o? *
7166 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7167 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP /*|MODE_VOICE */))
d76ed9a9
AS
7168 continue;
7169 /* Need not check for bans unless channel registration is active. */
7170 if(!channel->channel_info || IsSuspended(channel->channel_info))
7171 continue;
7172 /* Look for a matching ban already on the channel. */
7173 for(jj = 0; jj < channel->banlist.used; ++jj)
7174 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
7175 break;
7176 /* Need not act if we found one. */
7177 if(jj < channel->banlist.used)
7178 continue;
7179 /* Look for a matching ban in this channel. */
7180 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7181 {
7182 if(!user_matches_glob(user, bData->mask, 1))
7183 continue;
a32da4c7 7184 change.args[0].u.hostmask = bData->mask;
d76ed9a9
AS
7185 mod_chanmode_announce(chanserv, channel, &change);
7186 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7187 KickChannelUser(user, channel, chanserv, kick_reason);
7188 bData->triggered = now;
7189 break; /* we don't need to check any more bans in the channel */
7190 }
7191 }
7192}
7193
7194static void handle_rename(struct handle_info *handle, const char *old_handle)
7195{
7196 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7197
7198 if(dnr)
7199 {
7200 dict_remove2(handle_dnrs, old_handle, 1);
7201 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7202 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7203 }
7204}
7205
7206static void
7207handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7208{
7209 struct userNode *h_user;
7210
7211 if(handle->channels)
7212 {
7213 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7214 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7215
7216 while(handle->channels)
7217 del_channel_user(handle->channels, 1);
7218 }
7219}
7220
7221static void
7222handle_server_link(UNUSED_ARG(struct server *server))
7223{
7224 struct chanData *cData;
7225
7226 for(cData = channelList; cData; cData = cData->next)
7227 {
7228 if(!IsSuspended(cData))
7229 cData->may_opchan = 1;
7230 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7231 && !cData->channel->join_flooded
7232 && ((cData->channel->limit - cData->channel->members.used)
7233 < chanserv_conf.adjust_threshold))
7234 {
7235 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7236 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7237 }
7238 }
7239}
7240
7241static void
7242chanserv_conf_read(void)
7243{
7244 dict_t conf_node;
7245 const char *str;
7246 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7247 struct mod_chanmode *change;
7248 struct string_list *strlist;
7249 struct chanNode *chan;
7250 unsigned int ii;
7251
7252 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7253 {
7254 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7255 return;
7256 }
7257 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7258 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7259 chanserv_conf.support_channels.used = 0;
7260 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7261 {
7262 for(ii = 0; ii < strlist->used; ++ii)
7263 {
7264 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7265 if(!str2)
7266 str2 = "+nt";
2aef5f4b 7267 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
d76ed9a9
AS
7268 LockChannel(chan);
7269 channelList_append(&chanserv_conf.support_channels, chan);
7270 }
7271 }
7272 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7273 {
7274 const char *str2;
7275 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7276 if(!str2)
7277 str2 = "+nt";
2aef5f4b 7278 chan = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9
AS
7279 LockChannel(chan);
7280 channelList_append(&chanserv_conf.support_channels, chan);
7281 }
7282 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7283 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7284 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7285 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7286 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
1117fc5a 7287 chanserv_conf.greeting_length = str ? atoi(str) : 200;
d76ed9a9
AS
7288 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7289 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7290 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7291 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7292 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7293 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
31f23f13
AS
7294 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
7295 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
d76ed9a9
AS
7296 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7297 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7298 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7299 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7300 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7301 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7302 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7303 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7304 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7305 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7306 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7307 if(chanserv && str)
7308 NickChange(chanserv, str, 0);
7309 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7310 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
a32da4c7 7311 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7312 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
d76ed9a9
AS
7313 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7314 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7315 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7316 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7317 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7318 chanserv_conf.max_owned = str ? atoi(str) : 5;
7319 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7320 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7321 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7322 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7323 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7324 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7325 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7326 if(!str)
7327 str = "+nt";
7328 safestrncpy(mode_line, str, sizeof(mode_line));
7329 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7330 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
7331 {
7332 chanserv_conf.default_modes = *change;
7333 mod_chanmode_free(change);
7334 }
7335 free_string_list(chanserv_conf.set_shows);
7336 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7337 if(strlist)
7338 strlist = string_list_copy(strlist);
7339 else
7340 {
7341 static const char *list[] = {
7342 /* free form text */
7343 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7344 /* options based on user level */
4b6129c0
AS
7345 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7346 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
d76ed9a9 7347 /* multiple choice options */
4b6129c0 7348 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh",
d76ed9a9 7349 /* binary options */
31f23f13 7350 "DynLimit", "NoDelete", "BanTimeout",
d76ed9a9
AS
7351 /* delimiter */
7352 NULL
7353 };
7354 unsigned int ii;
7355 strlist = alloc_string_list(ArrayLength(list)-1);
7356 for(ii=0; list[ii]; ii++)
7357 string_list_append(strlist, strdup(list[ii]));
7358 }
7359 chanserv_conf.set_shows = strlist;
7360 /* We don't look things up now, in case the list refers to options
7361 * defined by modules initialized after this point. Just mark the
7362 * function list as invalid, so it will be initialized.
7363 */
7364 set_shows_list.used = 0;
7365 free_string_list(chanserv_conf.eightball);
7366 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7367 if(strlist)
7368 {
7369 strlist = string_list_copy(strlist);
7370 }
7371 else
7372 {
7373 strlist = alloc_string_list(4);
7374 string_list_append(strlist, strdup("Yes."));
7375 string_list_append(strlist, strdup("No."));
7376 string_list_append(strlist, strdup("Maybe so."));
7377 }
7378 chanserv_conf.eightball = strlist;
7379 free_string_list(chanserv_conf.old_ban_names);
7380 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7381 if(strlist)
7382 strlist = string_list_copy(strlist);
7383 else
7384 strlist = alloc_string_list(2);
7385 chanserv_conf.old_ban_names = strlist;
d76ed9a9 7386 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
1117fc5a 7387 off_channel = str ? atoi(str) : 0;
d76ed9a9
AS
7388}
7389
7390static void
7391chanserv_note_type_read(const char *key, struct record_data *rd)
7392{
7393 dict_t obj;
7394 struct note_type *ntype;
7395 const char *str;
7396
7397 if(!(obj = GET_RECORD_OBJECT(rd)))
7398 {
7399 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7400 return;
7401 }
7402 if(!(ntype = chanserv_create_note_type(key)))
7403 {
7404 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7405 return;
7406 }
7407
7408 /* Figure out set access */
7409 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7410 {
7411 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7412 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7413 }
7414 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7415 {
7416 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7417 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7418 }
7419 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7420 {
7421 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7422 }
7423 else
7424 {
7425 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7426 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7427 ntype->set_access.min_opserv = 0;
7428 }
7429
7430 /* Figure out visibility */
7431 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7432 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7433 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7434 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7435 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7436 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7437 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7438 ntype->visible_type = NOTE_VIS_ALL;
7439 else
7440 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7441
7442 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7443 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7444}
7445
7446static void
7447user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7448{
7449 struct handle_info *handle;
7450 struct userData *uData;
b10abdb2 7451 char *seen, *inf, *flags, *expires;
d76ed9a9
AS
7452 time_t last_seen;
7453 unsigned short access;
7454
7455 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7456 {
7457 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7458 return;
7459 }
7460
7461 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7462 if(access > UL_OWNER)
7463 {
7464 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7465 return;
7466 }
7467
7468 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7469 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7470 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7471 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
b10abdb2 7472 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
d76ed9a9
AS
7473 handle = get_handle_info(key);
7474 if(!handle)
7475 {
7476 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7477 return;
7478 }
7479
7480 uData = add_channel_user(chan, handle, access, last_seen, inf);
7481 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
b10abdb2 7482 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
7483
7484 if((uData->flags & USER_SUSPENDED) && uData->expires)
7485 {
7486 if(uData->expires > now)
7487 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
7488 else
7489 uData->flags &= ~USER_SUSPENDED;
7490 }
c8ca69a0
AS
7491
7492 /* Upgrade: set autoop to the inverse of noautoop */
7493 if(chanserv_read_version < 2)
7494 {
7495 /* if noautoop is true, set autoop false, and vice versa */
7496 if(uData->flags & USER_NOAUTO_OP)
7497 uData->flags = uData->flags & ~USER_AUTO_OP;
7498 else
7499 uData->flags = uData->flags | USER_AUTO_OP;
7500 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);
7501 }
7502
d76ed9a9
AS
7503}
7504
7505static void
7506ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7507{
7508 struct banData *bData;
7509 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7510 time_t set_time, triggered_time, expires_time;
7511
7512 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7513 {
7514 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7515 return;
7516 }
7517
7518 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7519 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7520 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7521 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7522 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7523 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
0d16e639 7524 if (!reason || !owner)
7525 return;
d76ed9a9
AS
7526
7527 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7528 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7529 if(s_expires)
7530 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7531 else if(s_duration)
7532 expires_time = set_time + atoi(s_duration);
7533 else
7534 expires_time = 0;
7535
ec1a68c8 7536 if(!reason || (expires_time && (expires_time < now)))
d76ed9a9
AS
7537 return;
7538
7539 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7540}
7541
7542static struct suspended *
7543chanserv_read_suspended(dict_t obj)
7544{
7545 struct suspended *suspended = calloc(1, sizeof(*suspended));
7546 char *str;
7547 dict_t previous;
7548
7549 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7550 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7551 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7552 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7553 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7554 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7555 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7556 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7557 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7558 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7559 return suspended;
7560}
7561
82f37c08 7562static struct giveownership *
7563chanserv_read_giveownership(dict_t obj)
7564{
7565 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
7566 char *str;
7567 dict_t previous;
7568
7569 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
7570 giveownership->staff_issuer = str ? strdup(str) : NULL;
7571
7572 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
7573
7574 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
7575 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
7576
7577 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
7578 giveownership->reason = str ? strdup(str) : NULL;
7579 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7580 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7581
7582 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7583 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
7584 return giveownership;
7585}
7586
d76ed9a9
AS
7587static int
7588chanserv_channel_read(const char *key, struct record_data *hir)
7589{
7590 struct suspended *suspended;
82f37c08 7591 struct giveownership *giveownership;
d76ed9a9
AS
7592 struct mod_chanmode *modes;
7593 struct chanNode *cNode;
7594 struct chanData *cData;
7595 struct dict *channel, *obj;
7596 char *str, *argv[10];
7597 dict_iterator_t it;
7598 unsigned int argc;
7599
7600 channel = hir->d.object;
7601
7602 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7603 if(!str)
7604 str = "<unknown>";
2aef5f4b 7605 cNode = AddChannel(key, now, NULL, NULL, NULL);
d76ed9a9
AS
7606 if(!cNode)
7607 {
7608 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7609 return 0;
7610 }
7611 cData = register_channel(cNode, str);
7612 if(!cData)
7613 {
7614 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7615 return 0;
7616 }
7617
7618 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7619 {
7620 enum levelOption lvlOpt;
7621 enum charOption chOpt;
7622
7623 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7624 cData->flags = atoi(str);
7625
7626 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7627 {
7628 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7629 if(str)
7630 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7631 else if(levelOptions[lvlOpt].old_flag)
7632 {
7633 if(cData->flags & levelOptions[lvlOpt].old_flag)
7634 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7635 else
7636 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7637 }
7638 }
7639
7640 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7641 {
7642 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7643 continue;
7644 cData->chOpts[chOpt] = str[0];
7645 }
7646 }
7647 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7648 {
7649 enum levelOption lvlOpt;
7650 enum charOption chOpt;
7651 unsigned int count;
7652
7653 cData->flags = base64toint(str, 5);
7654 count = strlen(str += 5);
7655 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7656 {
7657 unsigned short lvl;
7658 if(levelOptions[lvlOpt].old_flag)
7659 {
7660 if(cData->flags & levelOptions[lvlOpt].old_flag)
7661 lvl = levelOptions[lvlOpt].flag_value;
7662 else
7663 lvl = levelOptions[lvlOpt].default_value;
7664 }
7665 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7666 {
7667 case 'c': lvl = UL_COOWNER; break;
4048352e 7668 case 'm': lvl = UL_MANAGER; break;
d76ed9a9
AS
7669 case 'n': lvl = UL_OWNER+1; break;
7670 case 'o': lvl = UL_OP; break;
7671 case 'p': lvl = UL_PEON; break;
55342ce8 7672 case 'h': lvl = UL_HALFOP; break;
d76ed9a9
AS
7673 case 'w': lvl = UL_OWNER; break;
7674 default: lvl = 0; break;
7675 }
7676 cData->lvlOpts[lvlOpt] = lvl;
7677 }
7678 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7679 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7680 }
7681
7682 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7683 {
7684 suspended = chanserv_read_suspended(obj);
7685 cData->suspended = suspended;
7686 suspended->cData = cData;
7687 /* We could use suspended->expires and suspended->revoked to
7688 * set the CHANNEL_SUSPENDED flag, but we don't. */
7689 }
a32da4c7 7690 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
d76ed9a9
AS
7691 {
7692 suspended = calloc(1, sizeof(*suspended));
7693 suspended->issued = 0;
7694 suspended->revoked = 0;
a32da4c7 7695 suspended->suspender = strdup(str);
d76ed9a9
AS
7696 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7697 suspended->expires = str ? atoi(str) : 0;
d76ed9a9
AS
7698 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7699 suspended->reason = strdup(str ? str : "No reason");
7700 suspended->previous = NULL;
7701 cData->suspended = suspended;
7702 suspended->cData = cData;
7703 }
7704 else
a32da4c7 7705 {
7706 cData->flags &= ~CHANNEL_SUSPENDED;
d76ed9a9 7707 suspended = NULL; /* to squelch a warning */
a32da4c7 7708 }
d76ed9a9
AS
7709
7710 if(IsSuspended(cData)) {
7711 if(suspended->expires > now)
7712 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7713 else if(suspended->expires)
7714 cData->flags &= ~CHANNEL_SUSPENDED;
7715 }
7716
82f37c08 7717 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
7718 {
7719 giveownership = chanserv_read_giveownership(obj);
7720 cData->giveownership = giveownership;
7721 }
7722
d76ed9a9
AS
7723 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7724 struct mod_chanmode change;
7725 mod_chanmode_init(&change);
7726 change.argc = 1;
7727 change.args[0].mode = MODE_CHANOP;
a32da4c7 7728 change.args[0].u.member = AddChannelUser(chanserv, cNode);
d76ed9a9
AS
7729 mod_chanmode_announce(chanserv, cNode, &change);
7730 }
7731
7732 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7733 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7734 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7735 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
a32da4c7 7736 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7737 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
d76ed9a9
AS
7738 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7739 cData->max = str ? atoi(str) : 0;
7740 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7741 cData->greeting = str ? strdup(str) : NULL;
7742 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7743 cData->user_greeting = str ? strdup(str) : NULL;
7744 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7745 cData->topic_mask = str ? strdup(str) : NULL;
7746 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7747 cData->topic = str ? strdup(str) : NULL;
7748
7749 if(!IsSuspended(cData)
7750 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7751 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7752 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
7753 cData->modes = *modes;
1117fc5a 7754 if(off_channel > 0)
a32da4c7 7755 cData->modes.modes_set |= MODE_REGISTERED;
d76ed9a9
AS
7756 if(cData->modes.argc > 1)
7757 cData->modes.argc = 1;
7758 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7759 mod_chanmode_free(modes);
7760 }
7761
7762 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7763 for(it = dict_first(obj); it; it = iter_next(it))
7764 user_read_helper(iter_key(it), iter_data(it), cData);
7765
7766 if(!cData->users && !IsProtected(cData))
7767 {
7768 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7769 unregister_channel(cData, "has empty user list.");
7770 return 0;
7771 }
7772
7773 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7774 for(it = dict_first(obj); it; it = iter_next(it))
7775 ban_read_helper(iter_key(it), iter_data(it), cData);
7776
7777 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7778 for(it = dict_first(obj); it; it = iter_next(it))
7779 {
7780 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7781 struct record_data *rd = iter_data(it);
7782 const char *note, *setter;
7783
7784 if(rd->type != RECDB_OBJECT)
7785 {
7786 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7787 }
7788 else if(!ntype)
7789 {
7790 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7791 }
7792 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7793 {
7794 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7795 }
7796 else
7797 {
7798 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7799 if(!setter) setter = "<unknown>";
7800 chanserv_add_channel_note(cData, ntype, setter, note);
7801 }
7802 }
7803
7804 return 0;
7805}
7806
7807static void
7808chanserv_dnr_read(const char *key, struct record_data *hir)
7809{
7810 const char *setter, *reason, *str;
7811 struct do_not_register *dnr;
7812
7813 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7814 if(!setter)
7815 {
7816 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7817 return;
7818 }
7819 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7820 if(!reason)
7821 {
7822 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7823 return;
7824 }
7825 dnr = chanserv_add_dnr(key, setter, reason);
7826 if(!dnr)
7827 return;
7828 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7829 if(str)
7830 dnr->set = atoi(str);
7831 else
7832 dnr->set = 0;
7833}
7834
c8ca69a0
AS
7835static void
7836chanserv_version_read(struct dict *section)
7837{
7838 /* global var.. */
7839 char *str;
7840 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
7841 if(str)
7842 chanserv_read_version = atoi(str);
7843 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
7844}
7845
d76ed9a9
AS
7846static int
7847chanserv_saxdb_read(struct dict *database)
7848{
7849 struct dict *section;
7850 dict_iterator_t it;
7851
c8ca69a0
AS
7852 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
7853 chanserv_version_read(section);
7854
d76ed9a9
AS
7855 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7856 for(it = dict_first(section); it; it = iter_next(it))
7857 chanserv_note_type_read(iter_key(it), iter_data(it));
7858
7859 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7860 for(it = dict_first(section); it; it = iter_next(it))
7861 chanserv_channel_read(iter_key(it), iter_data(it));
7862
7863 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7864 for(it = dict_first(section); it; it = iter_next(it))
7865 chanserv_dnr_read(iter_key(it), iter_data(it));
7866
7867 return 0;
7868}
7869
7870static int
7871chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7872{
7873 int high_present = 0;
7874 saxdb_start_record(ctx, KEY_USERS, 1);
7875 for(; uData; uData = uData->next)
7876 {
7877 if((uData->access >= UL_PRESENT) && uData->present)
7878 high_present = 1;
7879 saxdb_start_record(ctx, uData->handle->handle, 0);
7880 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7881 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7882 if(uData->flags)
7883 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
b10abdb2 7884 if(uData->expires)
7885 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
d76ed9a9
AS
7886 if(uData->info)
7887 saxdb_write_string(ctx, KEY_INFO, uData->info);
7888 saxdb_end_record(ctx);
7889 }
7890 saxdb_end_record(ctx);
7891 return high_present;
7892}
7893
7894static void
7895chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7896{
7897 if(!bData)
7898 return;
7899 saxdb_start_record(ctx, KEY_BANS, 1);
7900 for(; bData; bData = bData->next)
7901 {
7902 saxdb_start_record(ctx, bData->mask, 0);
7903 saxdb_write_int(ctx, KEY_SET, bData->set);
7904 if(bData->triggered)
7905 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7906 if(bData->expires)
7907 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7908 if(bData->owner[0])
7909 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7910 if(bData->reason)
7911 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7912 saxdb_end_record(ctx);
7913 }
7914 saxdb_end_record(ctx);
7915}
7916
7917static void
7918chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7919{
7920 saxdb_start_record(ctx, name, 0);
7921 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7922 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7923 if(susp->issued)
7924 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7925 if(susp->expires)
7926 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7927 if(susp->revoked)
7928 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7929 if(susp->previous)
7930 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7931 saxdb_end_record(ctx);
7932}
7933
82f37c08 7934static void
7935chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
7936{
7937 saxdb_start_record(ctx, name, 0);
7938 if(giveownership->staff_issuer)
7939 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
7940 if(giveownership->old_owner)
7941 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
7942 if(giveownership->target)
7943 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
7944 if(giveownership->target_access)
7945 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
7946 if(giveownership->reason)
7947 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
7948 if(giveownership->issued)
7949 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
7950 if(giveownership->previous)
7951 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
7952 saxdb_end_record(ctx);
7953}
7954
d76ed9a9
AS
7955static void
7956chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7957{
7958 char buf[MAXLEN];
7959 int high_present;
7960 enum levelOption lvlOpt;
7961 enum charOption chOpt;
7962
7963 saxdb_start_record(ctx, channel->channel->name, 1);
7964
7965 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7966 saxdb_write_int(ctx, KEY_MAX, channel->max);
7967 if(channel->topic)
7968 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7969 if(channel->registrar)
7970 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7971 if(channel->greeting)
7972 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7973 if(channel->user_greeting)
7974 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7975 if(channel->topic_mask)
7976 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7977 if(channel->suspended)
7978 chanserv_write_suspended(ctx, "suspended", channel->suspended);
82f37c08 7979 if(channel->giveownership)
7980 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
d76ed9a9
AS
7981
7982 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7983 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7984 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7985 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7986 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7987 {
7988 buf[0] = channel->chOpts[chOpt];
7989 buf[1] = '\0';
7990 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7991 }
7992 saxdb_end_record(ctx);
7993
7994 if(channel->modes.modes_set || channel->modes.modes_clear)
7995 {
7996 mod_chanmode_format(&channel->modes, buf);
7997 saxdb_write_string(ctx, KEY_MODES, buf);
7998 }
7999
8000 high_present = chanserv_write_users(ctx, channel->users);
8001 chanserv_write_bans(ctx, channel->bans);
8002
8003 if(dict_size(channel->notes))
8004 {
8005 dict_iterator_t it;
8006
8007 saxdb_start_record(ctx, KEY_NOTES, 1);
8008 for(it = dict_first(channel->notes); it; it = iter_next(it))
8009 {
8010 struct note *note = iter_data(it);
8011 saxdb_start_record(ctx, iter_key(it), 0);
8012 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8013 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8014 saxdb_end_record(ctx);
8015 }
8016 saxdb_end_record(ctx);
8017 }
8018
a32da4c7 8019 if(channel->ownerTransfer)
8020 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
d76ed9a9
AS
8021 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8022 saxdb_end_record(ctx);
8023}
8024
8025static void
8026chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8027{
8028 const char *str;
8029
8030 saxdb_start_record(ctx, ntype->name, 0);
8031 switch(ntype->set_access_type)
8032 {
8033 case NOTE_SET_CHANNEL_ACCESS:
8034 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8035 break;
8036 case NOTE_SET_CHANNEL_SETTER:
8037 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8038 break;
8039 case NOTE_SET_PRIVILEGED: default:
8040 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8041 break;
8042 }
8043 switch(ntype->visible_type)
8044 {
8045 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8046 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8047 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8048 }
8049 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8050 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8051 saxdb_end_record(ctx);
8052}
8053
8054static void
8055write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8056{
8057 struct do_not_register *dnr;
8058 dict_iterator_t it;
8059
8060 for(it = dict_first(dnrs); it; it = iter_next(it))
8061 {
8062 dnr = iter_data(it);
8063 saxdb_start_record(ctx, dnr->chan_name, 0);
8064 if(dnr->set)
8065 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8066 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8067 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8068 saxdb_end_record(ctx);
8069 }
8070}
8071
8072static int
8073chanserv_saxdb_write(struct saxdb_context *ctx)
8074{
8075 dict_iterator_t it;
8076 struct chanData *channel;
8077
c8ca69a0
AS
8078 /* Version Control*/
8079 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
8080 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
8081 saxdb_end_record(ctx);
8082
d76ed9a9
AS
8083 /* Notes */
8084 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8085 for(it = dict_first(note_types); it; it = iter_next(it))
8086 chanserv_write_note_type(ctx, iter_data(it));
8087 saxdb_end_record(ctx);
8088
8089 /* DNRs */
8090 saxdb_start_record(ctx, KEY_DNR, 1);
8091 write_dnrs_helper(ctx, handle_dnrs);
8092 write_dnrs_helper(ctx, plain_dnrs);
8093 write_dnrs_helper(ctx, mask_dnrs);
8094 saxdb_end_record(ctx);
8095
8096 /* Channels */
8097 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8098 for(channel = channelList; channel; channel = channel->next)
8099 chanserv_write_channel(ctx, channel);
8100 saxdb_end_record(ctx);
8101
8102 return 0;
8103}
8104
8105static void
8106chanserv_db_cleanup(void) {
8107 unsigned int ii;
8108 unreg_part_func(handle_part);
8109 while(channelList)
8110 unregister_channel(channelList, "terminating.");
8111 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8112 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8113 free(chanserv_conf.support_channels.list);
8114 dict_delete(handle_dnrs);
8115 dict_delete(plain_dnrs);
8116 dict_delete(mask_dnrs);
8117 dict_delete(note_types);
8118 free_string_list(chanserv_conf.eightball);
8119 free_string_list(chanserv_conf.old_ban_names);
8120 free_string_list(chanserv_conf.set_shows);
8121 free(set_shows_list.list);
8122 free(uset_shows_list.list);
8123 while(helperList)
8124 {
8125 struct userData *helper = helperList;
8126 helperList = helperList->next;
8127 free(helper);
8128 }
8129}
8130
8131#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8132#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8133#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8134
8135void
8136init_chanserv(const char *nick)
8137{
8138 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8139 conf_register_reload(chanserv_conf_read);
8140
8141 reg_server_link_func(handle_server_link);
8142
8143 reg_new_channel_func(handle_new_channel);
8144 reg_join_func(handle_join);
8145 reg_part_func(handle_part);
8146 reg_kick_func(handle_kick);
8147 reg_topic_func(handle_topic);
8148 reg_mode_change_func(handle_mode);
8149 reg_nick_change_func(handle_nick_change);
8150
8151 reg_auth_func(handle_auth);
8152 reg_handle_rename_func(handle_rename);
8153 reg_unreg_func(handle_unreg);
8154
8155 handle_dnrs = dict_new();
8156 dict_set_free_data(handle_dnrs, free);
8157 plain_dnrs = dict_new();
8158 dict_set_free_data(plain_dnrs, free);
8159 mask_dnrs = dict_new();
8160 dict_set_free_data(mask_dnrs, free);
8161
8162 reg_svccmd_unbind_func(handle_svccmd_unbind);
8163 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8164 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8165 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8166 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8167 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8168 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8169 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8170 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8171 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8172
ac3bdc8d
AS
8173 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8174
d76ed9a9
AS
8175 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8176 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8177
4048352e
AS
8178 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8179 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8180 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8181 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9
AS
8182 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8183
8184 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8185 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
4048352e
AS
8186 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8187 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8188 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
55342ce8 8189 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8190
4048352e 8191 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8192 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
4048352e 8193 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9
AS
8194 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8195
8196 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8197 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8198 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8199 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
55342ce8 8200 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
d76ed9a9
AS
8201 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8202 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
55342ce8 8203 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
d76ed9a9
AS
8204 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8205 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8206
c8273589
AS
8207 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8208 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8209 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8210 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
8211 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
8212 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
d76ed9a9 8213 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
c8273589 8214 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
d76ed9a9
AS
8215 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8216 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
4048352e 8217 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
d76ed9a9 8218 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
4048352e
AS
8219 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8220 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8221
c8273589 8222 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
23475fc6 8223 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
c8273589
AS
8224 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8225 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8226
8227 /* if you change dellamer access, see also places
8228 * like unbanme which have manager hardcoded. */
8229 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
d76ed9a9
AS
8230 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8231
c8273589
AS
8232 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8233
d76ed9a9
AS
8234 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8235
8236 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8237 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8238 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8239 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8240 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8241 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8242 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
55342ce8 8243 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
d76ed9a9
AS
8244 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8245 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8246 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8247 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8248
8249 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8250 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8251
8252 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8253 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8254 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8255 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8256
8257 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8258 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8259 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8260 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8261 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8262
8263 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8264 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8265 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8266 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8267 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8268 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
b8daef0f 8269 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
d76ed9a9
AS
8270
8271 /* Channel options */
8272 DEFINE_CHANNEL_OPTION(defaulttopic);
8273 DEFINE_CHANNEL_OPTION(topicmask);
8274 DEFINE_CHANNEL_OPTION(greeting);
8275 DEFINE_CHANNEL_OPTION(usergreeting);
8276 DEFINE_CHANNEL_OPTION(modes);
8277 DEFINE_CHANNEL_OPTION(enfops);
55342ce8 8278 DEFINE_CHANNEL_OPTION(enfhalfops);
4b6129c0 8279 DEFINE_CHANNEL_OPTION(automode);
d76ed9a9
AS
8280 DEFINE_CHANNEL_OPTION(protect);
8281 DEFINE_CHANNEL_OPTION(enfmodes);
8282 DEFINE_CHANNEL_OPTION(enftopic);
8283 DEFINE_CHANNEL_OPTION(pubcmd);
d76ed9a9
AS
8284 DEFINE_CHANNEL_OPTION(userinfo);
8285 DEFINE_CHANNEL_OPTION(dynlimit);
8286 DEFINE_CHANNEL_OPTION(topicsnarf);
8287 DEFINE_CHANNEL_OPTION(nodelete);
8288 DEFINE_CHANNEL_OPTION(toys);
8289 DEFINE_CHANNEL_OPTION(setters);
8290 DEFINE_CHANNEL_OPTION(topicrefresh);
d76ed9a9 8291 DEFINE_CHANNEL_OPTION(ctcpreaction);
31f23f13 8292 DEFINE_CHANNEL_OPTION(bantimeout);
d76ed9a9 8293 DEFINE_CHANNEL_OPTION(inviteme);
1117fc5a 8294 if(off_channel > 1)
d76ed9a9
AS
8295 DEFINE_CHANNEL_OPTION(offchannel);
8296 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8297
8298 /* Alias set topic to set defaulttopic for compatibility. */
8299 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8300
8301 /* User options */
d76ed9a9
AS
8302 DEFINE_USER_OPTION(autoinvite);
8303 DEFINE_USER_OPTION(info);
c8ca69a0 8304 DEFINE_USER_OPTION(autoop);
d76ed9a9
AS
8305
8306 /* Alias uset autovoice to uset autoop. */
c8ca69a0 8307 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
d76ed9a9
AS
8308
8309 note_types = dict_new();
8310 dict_set_free_data(note_types, chanserv_deref_note_type);
8311 if(nick)
8312 {
a32da4c7 8313 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8314 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
d76ed9a9
AS
8315 service_register(chanserv)->trigger = '!';
8316 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8317 }
c8ca69a0 8318
d76ed9a9
AS
8319 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8320
8321 if(chanserv_conf.channel_expire_frequency)
8322 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8323
31f23f13
AS
8324 if(chanserv_conf.ban_timeout_frequency)
8325 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
8326
d76ed9a9
AS
8327 if(chanserv_conf.refresh_period)
8328 {
8329 time_t next_refresh;
8330 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8331 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8332 }
1117fc5a 8333
d76ed9a9
AS
8334 reg_exit_func(chanserv_db_cleanup);
8335 message_register_table(msgtab);
8336}