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