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