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