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