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