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