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