]> jfr.im git - irc/evilnet/x3.git/blame - src/chanserv.c
improved the information help bit, and .info command listing in help
[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 }
90e75ffd 6238 if(uData->access < UL_HALFOP /*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);
d76ed9a9 6242}
6243
6244static MODCMD_FUNC(user_opt_autoinvite)
6245{
6246 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6247}
6248
cd25f2e9 6249static MODCMD_FUNC(user_opt_autojoin)
6250{
6251 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN, CSFUNC_ARGS);
6252}
6253
d76ed9a9 6254static MODCMD_FUNC(user_opt_info)
6255{
6256 struct userData *uData;
6257 char *infoline;
6258
6259 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6260
6261 if(!uData)
6262 {
6263 /* If they got past the command restrictions (which require access)
6264 * but fail this test, we have some fool with security override on.
6265 */
6266 reply("CSMSG_NOT_USER", channel->name);
6267 return 0;
6268 }
6269
6270 if(argc > 1)
6271 {
6272 size_t bp;
6273 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6274 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6275 {
6276 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6277 return 0;
6278 }
6279 bp = strcspn(infoline, "\001");
6280 if(infoline[bp])
6281 {
6282 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6283 return 0;
6284 }
6285 if(uData->info)
6286 free(uData->info);
6287 if(infoline[0] == '*' && infoline[1] == 0)
6288 uData->info = NULL;
6289 else
6290 uData->info = strdup(infoline);
6291 }
6292 if(uData->info)
6293 reply("CSMSG_USET_INFO", uData->info);
6294 else
6295 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6296 return 1;
6297}
6298
6299struct svccmd_list uset_shows_list;
6300
6301static CHANSERV_FUNC(cmd_uset)
6302{
6303 struct svccmd *subcmd;
6304 char buf[MAXLEN];
6305 unsigned int ii;
6306
6307 /* Check if we need to (re-)initialize uset_shows_list. */
6308 if(!uset_shows_list.used)
6309 {
6310 char *options[] =
6311 {
cd25f2e9 6312 "AutoOp", "AutoInvite", "AutoJoin", "Info"
d76ed9a9 6313 };
6314
6315 if(!uset_shows_list.size)
6316 {
6317 uset_shows_list.size = ArrayLength(options);
6318 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6319 }
6320 for(ii = 0; ii < ArrayLength(options); ii++)
6321 {
6322 const char *name = options[ii];
6323 sprintf(buf, "%s %s", argv[0], name);
6324 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6325 if(!subcmd)
6326 {
6327 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6328 continue;
6329 }
6330 svccmd_list_append(&uset_shows_list, subcmd);
6331 }
6332 }
6333
6334 if(argc < 2)
6335 {
6336 /* Do this so options are presented in a consistent order. */
6337 reply("CSMSG_USER_OPTIONS");
6338 for(ii = 0; ii < uset_shows_list.used; ii++)
6339 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6340 return 1;
6341 }
6342
6343 sprintf(buf, "%s %s", argv[0], argv[1]);
6344 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6345 if(!subcmd)
6346 {
6347 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6348 return 0;
6349 }
6350
6351 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6352}
6353
6354static CHANSERV_FUNC(cmd_giveownership)
6355{
6356 struct handle_info *new_owner_hi;
6357 struct userData *new_owner, *curr_user;
6358 struct chanData *cData = channel->channel_info;
6359 struct do_not_register *dnr;
82f37c08 6360 struct giveownership *giveownership;
6361 unsigned int force, override;
6362 unsigned short co_access, new_owner_old_access;
09a3057c 6363 char transfer_reason[MAXLEN];
d76ed9a9 6364
6365 REQUIRE_PARAMS(2);
6366 curr_user = GetChannelAccess(cData, user->handle_info);
6367 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
82f37c08 6368
6369 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6370 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6371 && (uData->access > 500)
6372 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6373 || uData->access < 500));
6374
6375
d76ed9a9 6376 if(!curr_user || (curr_user->access != UL_OWNER))
6377 {
6378 struct userData *owner = NULL;
6379 for(curr_user = channel->channel_info->users;
6380 curr_user;
6381 curr_user = curr_user->next)
6382 {
6383 if(curr_user->access != UL_OWNER)
6384 continue;
6385 if(owner)
6386 {
6387 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6388 return 0;
6389 }
6390 owner = curr_user;
6391 }
6392 curr_user = owner;
6393 }
a32da4c7 6394 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
6395 {
6396 char delay[INTERVALLEN];
6397 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6398 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6399 return 0;
6400 }
5678501c 6401 if (!curr_user) {
6402 reply("CSMSG_NO_OWNER", channel->name);
6403 return 0;
6404 }
d76ed9a9 6405 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6406 return 0;
6407 if(new_owner_hi == user->handle_info)
6408 {
6409 reply("CSMSG_NO_TRANSFER_SELF");
6410 return 0;
6411 }
6412 new_owner = GetChannelAccess(cData, new_owner_hi);
6413 if(!new_owner)
6414 {
0d16e639 6415 if(force)
6416 {
6417 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
6418 }
6419 else
6420 {
6421 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6422 return 0;
6423 }
d76ed9a9 6424 }
6425 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6426 {
6427 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6428 return 0;
6429 }
6430 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6431 if(!IsHelping(user))
6432 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6433 else
1117fc5a 6434 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
d76ed9a9 6435 return 0;
6436 }
82f37c08 6437
6438 new_owner_old_access = new_owner->access;
d76ed9a9 6439 if(new_owner->access >= UL_COOWNER)
6440 co_access = new_owner->access;
6441 else
6442 co_access = UL_COOWNER;
6443 new_owner->access = UL_OWNER;
6444 if(curr_user)
6445 curr_user->access = co_access;
a32da4c7 6446 cData->ownerTransfer = now;
82f37c08 6447
6448 giveownership = calloc(1, sizeof(*giveownership));
6449 giveownership->issued = now;
6450 giveownership->old_owner = curr_user->handle->handle;
6451 giveownership->target = new_owner_hi->handle;
6452 giveownership->target_access = new_owner_old_access;
6453 if(override)
6454 {
6455 if(argc > (2 + force))
6456 {
6457 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6458 giveownership->reason = strdup(transfer_reason);
6459 }
6460 giveownership->staff_issuer = strdup(user->handle_info->handle);
6461 }
6462
6463 giveownership->previous = channel->channel_info->giveownership;
6464 channel->channel_info->giveownership = giveownership;
6465
d76ed9a9 6466 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
09a3057c 6467 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_OWNERSHIP_TRANSFERRED",
6468 channel->name, new_owner_hi->handle, user->handle_info->handle);
d76ed9a9 6469 return 1;
6470}
6471
b10abdb2 6472static void
6473chanserv_expire_user_suspension(void *data)
6474{
6475 struct userData *target = data;
6476
6477 target->expires = 0;
6478 target->flags &= ~USER_SUSPENDED;
6479}
6480
d76ed9a9 6481static CHANSERV_FUNC(cmd_suspend)
6482{
6483 struct handle_info *hi;
6484 struct userData *self, *target;
b10abdb2 6485 time_t expiry;
d76ed9a9 6486
b10abdb2 6487 REQUIRE_PARAMS(3);
d76ed9a9 6488 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6489 self = GetChannelUser(channel->channel_info, user->handle_info);
6490 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6491 {
6492 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6493 return 0;
6494 }
6495 if(target->access >= self->access)
6496 {
6497 reply("MSG_USER_OUTRANKED", hi->handle);
6498 return 0;
6499 }
6500 if(target->flags & USER_SUSPENDED)
6501 {
6502 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6503 return 0;
6504 }
6505 if(target->present)
6506 {
6507 target->present = 0;
6508 target->seen = now;
6509 }
b10abdb2 6510 if(!strcmp(argv[2], "0"))
6511 expiry = 0;
6512 else
6513 {
6514 unsigned int duration;
6515 if(!(duration = ParseInterval(argv[2])))
6516 {
6517 reply("MSG_INVALID_DURATION", argv[2]);
6518 return 0;
6519 }
6520 expiry = now + duration;
6521 }
6522
6523 target->expires = expiry;
6524
6525 if(target->expires)
6526 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6527
d76ed9a9 6528 target->flags |= USER_SUSPENDED;
6529 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6530 return 1;
6531}
6532
6533static CHANSERV_FUNC(cmd_unsuspend)
6534{
6535 struct handle_info *hi;
6536 struct userData *self, *target;
6537
6538 REQUIRE_PARAMS(2);
6539 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6540 self = GetChannelUser(channel->channel_info, user->handle_info);
6541 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6542 {
6543 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6544 return 0;
6545 }
6546 if(target->access >= self->access)
6547 {
6548 reply("MSG_USER_OUTRANKED", hi->handle);
6549 return 0;
6550 }
6551 if(!(target->flags & USER_SUSPENDED))
6552 {
6553 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6554 return 0;
6555 }
6556 target->flags &= ~USER_SUSPENDED;
a32da4c7 6557 scan_user_presence(target, NULL);
b10abdb2 6558 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
d76ed9a9 6559 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6560 return 1;
6561}
6562
6563static MODCMD_FUNC(cmd_deleteme)
6564{
6565 struct handle_info *hi;
6566 struct userData *target;
6567 const char *confirm_string;
6568 unsigned short access;
6569 char *channel_name;
6570
6571 hi = user->handle_info;
6572 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6573 {
6574 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6575 return 0;
6576 }
6577 if(target->access == UL_OWNER)
6578 {
6579 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6580 return 0;
6581 }
6582 confirm_string = make_confirmation_string(target);
6583 if((argc < 2) || strcmp(argv[1], confirm_string))
6584 {
6585 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6586 return 0;
6587 }
6588 access = target->access;
6589 channel_name = strdup(channel->name);
6590 del_channel_user(target, 1);
6591 reply("CSMSG_DELETED_YOU", access, channel_name);
6592 free(channel_name);
6593 return 1;
6594}
6595
6596static void
6597chanserv_refresh_topics(UNUSED_ARG(void *data))
6598{
6599 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6600 struct chanData *cData;
6601 char opt;
6602
6603 for(cData = channelList; cData; cData = cData->next)
6604 {
6605 if(IsSuspended(cData))
6606 continue;
6607 opt = cData->chOpts[chTopicRefresh];
6608 if(opt == 'n')
6609 continue;
6610 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6611 continue;
6612 if(cData->topic)
7fda2b52 6613 SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1);
d76ed9a9 6614 cData->last_refresh = refresh_num;
6615 }
6616 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6617}
6618
7637f48f 6619static void
6620chanserv_auto_resync(UNUSED_ARG(void *data))
6621{
6622 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6623 struct chanData *cData;
6624 char opt;
6625
6626 for(cData = channelList; cData; cData = cData->next)
6627 {
6628 if(IsSuspended(cData)) continue;
6629 opt = cData->chOpts[chResync];
6630 if(opt == 'n') continue;
6631 if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
6632 resync_channel(cData->channel);
6633 cData->last_resync = refresh_num;
6634 }
6635 timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
6636}
6637
d76ed9a9 6638static CHANSERV_FUNC(cmd_unf)
6639{
6640 if(channel)
6641 {
6642 char response[MAXLEN];
6643 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6644 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6645 irc_privmsg(cmd->parent->bot, channel->name, response);
6646 }
6647 else
6648 reply("CSMSG_UNF_RESPONSE");
6649 return 1;
6650}
6651
6652static CHANSERV_FUNC(cmd_ping)
6653{
6654 if(channel)
6655 {
6656 char response[MAXLEN];
6657 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6658 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6659 irc_privmsg(cmd->parent->bot, channel->name, response);
6660 }
6661 else
6662 reply("CSMSG_PING_RESPONSE");
6663 return 1;
6664}
6665
6666static CHANSERV_FUNC(cmd_wut)
6667{
6668 if(channel)
6669 {
6670 char response[MAXLEN];
6671 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6672 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6673 irc_privmsg(cmd->parent->bot, channel->name, response);
6674 }
6675 else
6676 reply("CSMSG_WUT_RESPONSE");
6677 return 1;
6678}
6679
240a3274 6680#ifdef lame8ball
d76ed9a9 6681static CHANSERV_FUNC(cmd_8ball)
6682{
6683 unsigned int i, j, accum;
6684 const char *resp;
6685
6686 REQUIRE_PARAMS(2);
6687 accum = 0;
6688 for(i=1; i<argc; i++)
6689 for(j=0; argv[i][j]; j++)
6690 accum = (accum << 5) - accum + toupper(argv[i][j]);
6691 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6692 if(channel)
6693 {
6694 char response[MAXLEN];
6695 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6696 irc_privmsg(cmd->parent->bot, channel->name, response);
6697 }
6698 else
6699 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6700 return 1;
6701}
6702
240a3274 6703#else /* Use cool 8ball instead */
6704
6705void eightball(char *outcome, int method, unsigned int seed)
6706{
6707 int answer = 0;
6708
6709#define NUMOFCOLORS 18
6710 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
6711 "white", "black", "grey", "brown",
6712 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6713 "fuchsia","turquoise","magenta", "cyan"};
6714#define NUMOFLOCATIONS 50
6715 char balllocations[50][55] = {
6716 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6717 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6718 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6719 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6720 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6721 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6722 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6723 "your bra", "your hair", "your bed", "the couch", "the wall",
6724 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6725 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6726#define NUMOFPREPS 15
6727 char ballpreps[50][50] = {
6728 "Near", "Somewhere near", "In", "In", "In",
6729 "In", "Hiding in", "Under", "Next to", "Over",
6730 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6731#define NUMOFNUMS 34
6732 char ballnums[50][50] = {
6733 "A hundred", "A thousand", "A few", "42",
6734 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6735 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6736 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6737 };
6738#define NUMOFMULTS 8
6739 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6740
6741 /* Method:
6742 * 0: normal (Not used in x3)
6743 * 1: color
6744 * 2: where is
6745 * 3: how many
6746 */
6747
6748 srand(seed);
6749 if (method == 1) /* A Color */
6750 {
6751 char tmp[MAXLEN];
6752
6753 answer = (rand() % 12); /* Make sure this is the # of entries */
6754 switch(answer)
6755 {
6756 case 0: strcpy(tmp, "Very bright %s, I'd say.");
6757 break;
6758 case 1: strcpy(tmp, "Sort of a light %s color.");
6759 break;
6760 case 2: strcpy(tmp, "Dark and dreary %s.");
6761 break;
6762 case 3: strcpy(tmp, "Quite a pale shade of %s.");
6763 break;
6764 case 4: strcpy(tmp, "A gross kind of mucky %s.");
6765 break;
6766 case 5: strcpy(tmp, "Brilliant whiteish %s.");
338a82b5 6767 break;
6768 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
6769 break;
6770 case 10: strcpy(tmp, "Solid %s.");
6771 break;
6772 case 11: strcpy(tmp, "Transparent %s.");
6773 break;
240a3274 6774 default: strcpy(outcome, "An invalid random number was generated.");
6775 return;
6776 }
6777 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
6778 return;
6779 }
6780 else if (method == 2) /* Location */
6781 {
6782 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
6783 }
6784 else if (method == 3) /* Number of ___ */
6785 {
6786 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
6787 }
6788 else
6789 {
6790 //Debug(DBGWARNING, "Error in 8ball.");
6791 }
6792 return;
6793}
6794
6795static CHANSERV_FUNC(cmd_8ball)
6796{
6797 char *word1, *word2, *word3;
6798 static char eb[MAXLEN];
6799 unsigned int accum, i, j;
6800
132f7859 6801 REQUIRE_PARAMS(2);
240a3274 6802 accum = 0;
6803 for(i=1; i<argc; i++)
6804 for(j=0; argv[i][j]; j++)
6805 accum = (accum << 5) - accum + toupper(argv[i][j]);
6806
6807 accum += time(NULL)/3600;
6808 word1 = argv[1];
6809 word2 = argc>2?argv[2]:"";
6810 word3 = argc>3?argv[3]:"";
6811
6812/*** COLOR *****/
6813 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
6814 eightball(eb, 1, accum);
6815 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6816 eightball(eb, 1, accum);
6817 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6818 eightball(eb, 1, accum);
6819/*** LOCATION *****/
6820 else if(
6821 (
6822 word2 &&
6823 (
6824 (strcasecmp(word1, "where") == 0) &&
6825 (strcasecmp(word2, "is") == 0)
6826 )
6827 ) ||
6828 (
6829 strcasecmp(word1, "where's") == 0
6830 )
6831 )
6832 eightball(eb, 2, accum);
6833/*** NUMBER *****/
6834 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
6835 eightball(eb, 3, accum);
6836/*** GENERIC *****/
6837 else
6838 {
6839 /* Generic 8ball question.. so pull from x3.conf srvx style */
6840 const char *resp;
6841
6842 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6843 if(channel)
6844 {
6845 char response[MAXLEN];
6846 sprintf(response, "\002%s\002: %s", user->nick, resp);
6847 irc_privmsg(cmd->parent->bot, channel->name, response);
6848 }
6849 else
6850 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6851 return 1;
6852 }
6853
6854 if(channel)
6855 {
6856 char response[MAXLEN];
6857 sprintf(response, "\002%s\002: %s", user->nick, eb);
6858 irc_privmsg(cmd->parent->bot, channel->name, response);
6859 }
6860 else
6861 send_message_type(4, user, cmd->parent->bot, "%s", eb);
6862 return 1;
6863}
6864#endif
6865
d76ed9a9 6866static CHANSERV_FUNC(cmd_d)
6867{
6868 unsigned long sides, count, modifier, ii, total;
6869 char response[MAXLEN], *sep;
6870 const char *fmt;
6871
6872 REQUIRE_PARAMS(2);
6873 if((count = strtoul(argv[1], &sep, 10)) < 1)
6874 goto no_dice;
6875 if(sep[0] == 0)
6876 {
6877 if(count == 1)
6878 goto no_dice;
6879 sides = count;
6880 count = 1;
6881 modifier = 0;
6882 }
6883 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6884 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6885 {
6886 if(sep[0] == 0)
6887 modifier = 0;
6888 else if((sep[0] == '-') && isdigit(sep[1]))
6889 modifier = strtoul(sep, NULL, 10);
6890 else if((sep[0] == '+') && isdigit(sep[1]))
6891 modifier = strtoul(sep+1, NULL, 10);
6892 else
6893 goto no_dice;
6894 }
6895 else
6896 {
6897 no_dice:
6898 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6899 return 0;
6900 }
6901 if(count > 10)
6902 {
6903 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6904 return 0;
6905 }
6906 for(total = ii = 0; ii < count; ++ii)
6907 total += (rand() % sides) + 1;
6908 total += modifier;
6909
6910 if((count > 1) || modifier)
6911 {
6912 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6913 sprintf(response, fmt, total, count, sides, modifier);
6914 }
6915 else
6916 {
6917 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6918 sprintf(response, fmt, total, sides);
6919 }
6920 if(channel)
6921 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6922 else
6923 send_message_type(4, user, cmd->parent->bot, "%s", response);
6924 return 1;
6925}
6926
6927static CHANSERV_FUNC(cmd_huggle)
6928{
6929 /* CTCP must be via PRIVMSG, never notice */
6930 if(channel)
6931 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6932 else
6933 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6934 return 1;
6935}
6936
e5a8f7cd 6937static CHANSERV_FUNC(cmd_calc)
6938{
6939 char response[MAXLEN];
6940
6941 REQUIRE_PARAMS(2);
6942 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
6943
6944 if(channel)
d8cf9c21 6945 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
e5a8f7cd 6946 else
6947 send_message_type(4, user, cmd->parent->bot, "%s", response);
6948 return 1;
6949}
6950
33741441 6951static CHANSERV_FUNC(cmd_reply)
6952{
6953
6954 REQUIRE_PARAMS(2);
6955 unsplit_string(argv + 1, argc - 1, NULL);
6956
6957 if(channel)
6958 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, unsplit_string(argv + 1, argc - 1, NULL));
6959 else
6960 send_message_type(4, user, cmd->parent->bot, "%s", unsplit_string(argv + 1, argc - 1, NULL));
6961 return 1;
6962}
6963
d76ed9a9 6964static void
6965chanserv_adjust_limit(void *data)
6966{
6967 struct mod_chanmode change;
6968 struct chanData *cData = data;
6969 struct chanNode *channel = cData->channel;
6970 unsigned int limit;
6971
6972 if(IsSuspended(cData))
6973 return;
6974
6975 cData->limitAdjusted = now;
6976 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6977 if(cData->modes.modes_set & MODE_LIMIT)
6978 {
6979 if(limit > cData->modes.new_limit)
6980 limit = cData->modes.new_limit;
6981 else if(limit == cData->modes.new_limit)
6982 return;
6983 }
6984
6985 mod_chanmode_init(&change);
6986 change.modes_set = MODE_LIMIT;
6987 change.new_limit = limit;
6988 mod_chanmode_announce(chanserv, channel, &change);
6989}
6990
6991static void
6992handle_new_channel(struct chanNode *channel)
6993{
6994 struct chanData *cData;
6995
6996 if(!(cData = channel->channel_info))
6997 return;
6998
6999 if(cData->modes.modes_set || cData->modes.modes_clear)
7000 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7001
7002 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7fda2b52 7003 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
d76ed9a9 7004}
7005
7006/* Welcome to my worst nightmare. Warning: Read (or modify)
7007 the code below at your own risk. */
7008static int
7009handle_join(struct modeNode *mNode)
7010{
7011 struct mod_chanmode change;
7012 struct userNode *user = mNode->user;
7013 struct chanNode *channel = mNode->channel;
7014 struct chanData *cData;
7015 struct userData *uData = NULL;
7016 struct banData *bData;
7017 struct handle_info *handle;
7018 unsigned int modes = 0, info = 0;
7019 char *greeting;
7020
7021 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7022 return 0;
7023
7024 cData = channel->channel_info;
7025 if(channel->members.used > cData->max)
7026 cData->max = channel->members.used;
7027
12673a59 7028#ifdef notdef
2f61d1d7 7029 /* Check for bans. If they're joining through a ban, one of two
7030 * cases applies:
7031 * 1: Join during a netburst, by riding the break. Kick them
7032 * unless they have ops or voice in the channel.
7033 * 2: They're allowed to join through the ban (an invite in
7034 * ircu2.10, or a +e on Hybrid, or something).
7035 * If they're not joining through a ban, and the banlist is not
7036 * full, see if they're on the banlist for the channel. If so,
7037 * kickban them.
7038 */
7039 if(user->uplink->burst && !mNode->modes)
7040 {
7041 unsigned int ii;
7042 for(ii = 0; ii < channel->banlist.used; ii++)
7043 {
7044 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7045 {
7046 /* Riding a netburst. Naughty. */
7047 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7048 return 1;
7049 }
7050 }
7051 }
12673a59 7052#endif
2f61d1d7 7053
d76ed9a9 7054 mod_chanmode_init(&change);
7055 change.argc = 1;
7056 if(channel->banlist.used < MAXBANS)
7057 {
7058 /* Not joining through a ban. */
7059 for(bData = cData->bans;
2f61d1d7 7060 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7061 bData = bData->next);
d76ed9a9 7062
7063 if(bData)
7064 {
7065 char kick_reason[MAXLEN];
7066 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7067
7068 bData->triggered = now;
7069 if(bData != cData->bans)
7070 {
7071 /* Shuffle the ban to the head of the list. */
7072 if(bData->next)
7073 bData->next->prev = bData->prev;
7074 if(bData->prev)
7075 bData->prev->next = bData->next;
7076
7077 bData->prev = NULL;
7078 bData->next = cData->bans;
7079
7080 if(cData->bans)
7081 cData->bans->prev = bData;
7082 cData->bans = bData;
7083 }
7084
7085 change.args[0].mode = MODE_BAN;
a32da4c7 7086 change.args[0].u.hostmask = bData->mask;
d76ed9a9 7087 mod_chanmode_announce(chanserv, channel, &change);
7088 KickChannelUser(user, channel, chanserv, kick_reason);
7089 return 1;
7090 }
7091 }
7092
7093 /* ChanServ will not modify the limits in join-flooded channels.
7094 It will also skip DynLimit processing when the user (or srvx)
7095 is bursting in, because there are likely more incoming. */
7096 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7097 && !user->uplink->burst
7098 && !channel->join_flooded
7099 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7100 {
7101 /* The user count has begun "bumping" into the channel limit,
7102 so set a timer to raise the limit a bit. Any previous
7103 timers are removed so three incoming users within the delay
7104 results in one limit change, not three. */
7105
7106 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7107 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7108 }
7109
4b6129c0 7110 /* Give automodes exept during join-floods */
7111 if(!channel->join_flooded)
d76ed9a9 7112 {
4b6129c0 7113 if(cData->chOpts[chAutomode] == 'v')
7114 modes |= MODE_VOICE;
7115 else if(cData->chOpts[chAutomode] == 'h')
7116 modes |= MODE_HALFOP;
7117 else if(cData->chOpts[chAutomode] == 'o')
7118 modes |= MODE_CHANOP;
d76ed9a9 7119 }
c8ca69a0 7120
d76ed9a9 7121 greeting = cData->greeting;
7122 if(user->handle_info)
7123 {
7124 handle = user->handle_info;
7125
7126 if(IsHelper(user) && !IsHelping(user))
7127 {
7128 unsigned int ii;
7129 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7130 {
7131 if(channel == chanserv_conf.support_channels.list[ii])
7132 {
7133 HANDLE_SET_FLAG(user->handle_info, HELPING);
7134 break;
7135 }
7136 }
7137 }
7138
7139 uData = GetTrueChannelAccess(cData, handle);
7140 if(uData && !IsUserSuspended(uData))
7141 {
4b6129c0 7142 /* non users getting automodes are handled above. */
7143 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 7144 {
4d69a3b1 7145 /* just op everyone with access */
7146 if(uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
7147 modes |= MODE_VOICE;
7148 /* or do their access level */
7149 else if(uData->access >= UL_OP )
d76ed9a9 7150 modes |= MODE_CHANOP;
4b6129c0 7151 else if(uData->access >= UL_HALFOP )
55342ce8 7152 modes |= MODE_HALFOP;
4b6129c0 7153 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
d76ed9a9 7154 modes |= MODE_VOICE;
7155 }
7156 if(uData->access >= UL_PRESENT)
7157 cData->visited = now;
7158 if(cData->user_greeting)
7159 greeting = cData->user_greeting;
7160 if(uData->info
7161 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7162 && ((now - uData->seen) >= chanserv_conf.info_delay)
7163 && !uData->present)
7164 info = 1;
7165 uData->seen = now;
7166 uData->present = 1;
7167 }
7168 }
2f61d1d7 7169
7170 /* If user joining normally (not during burst), apply op or voice,
7171 * and send greeting/userinfo as appropriate.
7172 */
d76ed9a9 7173 if(!user->uplink->burst)
7174 {
7175 if(modes)
7176 {
4b6129c0 7177 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
55342ce8 7178 if(modes & MODE_CHANOP) {
7179 modes &= ~MODE_HALFOP;
d76ed9a9 7180 modes &= ~MODE_VOICE;
55342ce8 7181 }
4b6129c0 7182 */
d76ed9a9 7183 change.args[0].mode = modes;
a32da4c7 7184 change.args[0].u.member = mNode;
d76ed9a9 7185 mod_chanmode_announce(chanserv, channel, &change);
7186 }
2f61d1d7 7187 if(greeting)
d76ed9a9 7188 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7189 if(uData && info)
7190 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7191 }
7192 return 0;
7193}
7194
7195static void
7196handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7197{
7198 struct mod_chanmode change;
7199 struct userData *channel;
5177fd21 7200 unsigned int ii, jj, i;
d76ed9a9 7201
7202 if(!user->handle_info)
7203 return;
7204
7205 mod_chanmode_init(&change);
7206 change.argc = 1;
7207 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7208 {
7209 struct chanNode *cn;
7210 struct modeNode *mn;
7211 if(IsUserSuspended(channel)
7212 || IsSuspended(channel->channel)
7213 || !(cn = channel->channel->channel))
7214 continue;
7215
7216 mn = GetUserMode(cn, user);
7217 if(!mn)
7218 {
7219 if(!IsUserSuspended(channel)
7220 && IsUserAutoInvite(channel)
7221 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7222 && !self->burst
7223 && !user->uplink->burst)
7224 irc_invite(chanserv, user, cn);
cd25f2e9 7225
7226 if(!IsUserSuspended(channel)
7227 && IsUserAutoJoin(channel)
7228 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7229 && !self->burst
7230 && !user->uplink->burst)
7231 irc_svsjoin(chanserv, user, cn);
7232
d76ed9a9 7233 continue;
7234 }
7235
7236 if(channel->access >= UL_PRESENT)
7237 channel->channel->visited = now;
7238
7239 if(IsUserAutoOp(channel))
7240 {
4b6129c0 7241 if(channel->access >= UL_OP )
d76ed9a9 7242 change.args[0].mode = MODE_CHANOP;
4b6129c0 7243 else if(channel->access >= UL_HALFOP )
55342ce8 7244 change.args[0].mode = MODE_HALFOP;
4b6129c0 7245 else if(channel->access >= UL_PEON )
d76ed9a9 7246 change.args[0].mode = MODE_VOICE;
7247 else
7248 change.args[0].mode = 0;
a32da4c7 7249 change.args[0].u.member = mn;
d76ed9a9 7250 if(change.args[0].mode)
7251 mod_chanmode_announce(chanserv, cn, &change);
7252 }
7253
7254 channel->seen = now;
7255 channel->present = 1;
7256 }
7257
7258 for(ii = 0; ii < user->channels.used; ++ii)
7259 {
7260 struct chanNode *channel = user->channels.list[ii]->channel;
7261 struct banData *ban;
7262
55342ce8 7263 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
1117fc5a 7264 || !channel->channel_info
7265 || IsSuspended(channel->channel_info))
d76ed9a9 7266 continue;
7267 for(jj = 0; jj < channel->banlist.used; ++jj)
2f61d1d7 7268 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
d76ed9a9 7269 break;
7270 if(jj < channel->banlist.used)
7271 continue;
7272 for(ban = channel->channel_info->bans; ban; ban = ban->next)
7273 {
7274 char kick_reason[MAXLEN];
2f61d1d7 7275 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9 7276 continue;
7277 change.args[0].mode = MODE_BAN;
a32da4c7 7278 change.args[0].u.hostmask = ban->mask;
d76ed9a9 7279 mod_chanmode_announce(chanserv, channel, &change);
7280 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7281 KickChannelUser(user, channel, chanserv, kick_reason);
7282 ban->triggered = now;
7283 break;
7284 }
7285 }
7286
7287 if(IsSupportHelper(user))
7288 {
7289 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7290 {
7291 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7292 {
7293 HANDLE_SET_FLAG(user->handle_info, HELPING);
7294 break;
7295 }
7296 }
7297 }
5177fd21 7298
7299 if (user->handle_info->ignores->used) {
7300 for (i=0; i < user->handle_info->ignores->used; i++) {
7301 irc_silence(user, user->handle_info->ignores->list[i], 1);
7302 }
7303 }
56958740 7304
7305 if (user->handle_info->epithet)
7306 irc_swhois(chanserv, user, user->handle_info->epithet);
d76ed9a9 7307}
7308
7309static void
7310handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7311{
7312 struct chanData *cData;
7313 struct userData *uData;
7314
7315 cData = mn->channel->channel_info;
7316 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7317 return;
7318
7319 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7320 {
7321 /* Allow for a bit of padding so that the limit doesn't
7322 track the user count exactly, which could get annoying. */
7323 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7324 {
7325 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7326 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7327 }
7328 }
7329
7330 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7331 {
7332 scan_user_presence(uData, mn->user);
7333 uData->seen = now;
7334 }
7335
7336 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7337 {
7338 unsigned int ii, jj;
7339 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7340 {
7341 for(jj = 0; jj < mn->user->channels.used; ++jj)
7342 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
7343 break;
7344 if(jj < mn->user->channels.used)
7345 break;
7346 }
7347 if(ii == chanserv_conf.support_channels.used)
7348 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7349 }
7350}
7351
7352static void
7353handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7354{
7355 struct userData *uData;
7356
7357 if(!channel->channel_info || !kicker || IsService(kicker)
7358 || (kicker == victim) || IsSuspended(channel->channel_info)
7359 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7360 return;
7361
7362 if(protect_user(victim, kicker, channel->channel_info))
7363 {
31f23f13 7364 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
d76ed9a9 7365 KickChannelUser(kicker, channel, chanserv, reason);
7366 }
7367
7368 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7369 uData->seen = now;
7370}
7371
7372static int
7373handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7374{
7375 struct chanData *cData;
7376
7377 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7378 return 0;
7379
7380 cData = channel->channel_info;
7381 if(bad_topic(channel, user, channel->topic))
b75e24a3 7382 { /* User doesnt have privs to set topics. Undo it */
d76ed9a9 7383 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7fda2b52 7384 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
d76ed9a9 7385 return 1;
7386 }
b75e24a3 7387 /* If there is a topic mask set, and the new topic doesnt match,
7388 * set the topic to mask + new_topic */
7389 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
7390 {
7391 char new_topic[TOPICLEN+1];
7392 conform_topic(cData->topic_mask, channel->topic, new_topic);
7393 if(*new_topic)
7394 {
75ef8cdc 7395 SetChannelTopic(channel, chanserv, user, new_topic, 1);
b75e24a3 7396 /* and fall through to topicsnarf code below.. */
7397 }
7398 else /* Topic couldnt fit into mask, was too long */
7399 {
75ef8cdc 7400 SetChannelTopic(channel, chanserv, user, old_topic, 1);
b75e24a3 7401 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
7402 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
7403 return 1;
7404 }
7405 }
d76ed9a9 7406 /* With topicsnarf, grab the topic and save it as the default topic. */
7407 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7408 {
7409 free(cData->topic);
7410 cData->topic = strdup(channel->topic);
7411 }
7412 return 0;
7413}
7414
7415static void
7416handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7417{
7418 struct mod_chanmode *bounce = NULL;
7419 unsigned int bnc, ii;
7420 char deopped = 0;
7421
7422 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7423 return;
7424
7425 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7426 && mode_lock_violated(&channel->channel_info->modes, change))
7427 {
7428 char correct[MAXLEN];
7429 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7430 mod_chanmode_format(&channel->channel_info->modes, correct);
7431 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7432 }
7433 for(ii = bnc = 0; ii < change->argc; ++ii)
7434 {
7435 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7436 {
a32da4c7 7437 const struct userNode *victim = change->args[ii].u.member->user;
d76ed9a9 7438 if(!protect_user(victim, user, channel->channel_info))
7439 continue;
7440 if(!bounce)
7441 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7442 if(!deopped)
7443 {
7444 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 7445 bounce->args[bnc].u.member = GetUserMode(channel, user);
7446 if(bounce->args[bnc].u.member)
d76ed9a9 7447 bnc++;
7448 deopped = 1;
7449 }
7450 bounce->args[bnc].mode = MODE_CHANOP;
a32da4c7 7451 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9 7452 bnc++;
7453 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7454 }
7455 else if(change->args[ii].mode & MODE_CHANOP)
7456 {
a32da4c7 7457 const struct userNode *victim = change->args[ii].u.member->user;
c092fcad 7458 if(IsService(victim) || validate_op(NULL, user, channel, (struct userNode*)victim))
d76ed9a9 7459 continue;
7460 if(!bounce)
7461 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7462 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 7463 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9 7464 bnc++;
7465 }
7466 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7467 {
a32da4c7 7468 const char *ban = change->args[ii].u.hostmask;
d76ed9a9 7469 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7470 continue;
7471 if(!bounce)
7472 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7473 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 7474 bounce->args[bnc].u.hostmask = strdup(ban);
d76ed9a9 7475 bnc++;
7476 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7477 }
7478 }
7479 if(bounce)
7480 {
7481 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7482 mod_chanmode_announce(chanserv, channel, bounce);
ec1a68c8 7483 for(ii = 0; ii < change->argc; ++ii)
7484 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7485 free((char*)bounce->args[ii].u.hostmask);
d76ed9a9 7486 mod_chanmode_free(bounce);
7487 }
7488}
7489
7490static void
7491handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7492{
7493 struct chanNode *channel;
7494 struct banData *bData;
7495 struct mod_chanmode change;
7496 unsigned int ii, jj;
7497 char kick_reason[MAXLEN];
7498
7499 mod_chanmode_init(&change);
7500 change.argc = 1;
7501 change.args[0].mode = MODE_BAN;
7502 for(ii = 0; ii < user->channels.used; ++ii)
7503 {
7504 channel = user->channels.list[ii]->channel;
7505 /* Need not check for bans if they're opped or voiced. */
4b6129c0 7506 /* TODO: does this make sense in automode v, h, and o? *
7507 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7508 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP /*|MODE_VOICE */))
d76ed9a9 7509 continue;
7510 /* Need not check for bans unless channel registration is active. */
7511 if(!channel->channel_info || IsSuspended(channel->channel_info))
7512 continue;
7513 /* Look for a matching ban already on the channel. */
7514 for(jj = 0; jj < channel->banlist.used; ++jj)
2f61d1d7 7515 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
d76ed9a9 7516 break;
7517 /* Need not act if we found one. */
7518 if(jj < channel->banlist.used)
7519 continue;
7520 /* Look for a matching ban in this channel. */
7521 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7522 {
2f61d1d7 7523 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9 7524 continue;
a32da4c7 7525 change.args[0].u.hostmask = bData->mask;
d76ed9a9 7526 mod_chanmode_announce(chanserv, channel, &change);
7527 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7528 KickChannelUser(user, channel, chanserv, kick_reason);
7529 bData->triggered = now;
7530 break; /* we don't need to check any more bans in the channel */
7531 }
7532 }
7533}
7534
7535static void handle_rename(struct handle_info *handle, const char *old_handle)
7536{
7537 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7538
7539 if(dnr)
7540 {
7541 dict_remove2(handle_dnrs, old_handle, 1);
7542 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7543 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7544 }
7545}
7546
7547static void
7548handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7549{
7550 struct userNode *h_user;
7551
7552 if(handle->channels)
7553 {
7554 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7555 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7556
7557 while(handle->channels)
7558 del_channel_user(handle->channels, 1);
7559 }
7560}
7561
7562static void
7563handle_server_link(UNUSED_ARG(struct server *server))
7564{
7565 struct chanData *cData;
7566
7567 for(cData = channelList; cData; cData = cData->next)
7568 {
7569 if(!IsSuspended(cData))
7570 cData->may_opchan = 1;
7571 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7572 && !cData->channel->join_flooded
7573 && ((cData->channel->limit - cData->channel->members.used)
7574 < chanserv_conf.adjust_threshold))
7575 {
7576 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7577 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7578 }
7579 }
7580}
7581
7582static void
7583chanserv_conf_read(void)
7584{
7585 dict_t conf_node;
7586 const char *str;
7587 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7588 struct mod_chanmode *change;
7589 struct string_list *strlist;
7590 struct chanNode *chan;
7591 unsigned int ii;
7592
7593 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7594 {
7595 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7596 return;
7597 }
7598 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7599 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7600 chanserv_conf.support_channels.used = 0;
7601 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7602 {
7603 for(ii = 0; ii < strlist->used; ++ii)
7604 {
7605 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7606 if(!str2)
7607 str2 = "+nt";
2aef5f4b 7608 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
d76ed9a9 7609 LockChannel(chan);
7610 channelList_append(&chanserv_conf.support_channels, chan);
7611 }
7612 }
7613 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7614 {
7615 const char *str2;
7616 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7617 if(!str2)
7618 str2 = "+nt";
2aef5f4b 7619 chan = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9 7620 LockChannel(chan);
7621 channelList_append(&chanserv_conf.support_channels, chan);
7622 }
7623 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7624 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7625 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7626 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7627 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
1117fc5a 7628 chanserv_conf.greeting_length = str ? atoi(str) : 200;
d76ed9a9 7629 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7630 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7631 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7632 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7633 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7634 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
31f23f13 7635 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
7636 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
d76ed9a9 7637 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7638 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7639 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7640 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7641 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7642 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7643 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7644 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7645 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7646 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7647 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7648 if(chanserv && str)
7649 NickChange(chanserv, str, 0);
7650 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7651 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
a32da4c7 7652 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7653 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
d76ed9a9 7654 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7655 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7656 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7657 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7658 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7659 chanserv_conf.max_owned = str ? atoi(str) : 5;
7660 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7661 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7662 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7663 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7664 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7665 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7637f48f 7666 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
7667 god_timeout = str ? ParseInterval(str) : 60*15;
d76ed9a9 7668 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7669 if(!str)
7670 str = "+nt";
7671 safestrncpy(mode_line, str, sizeof(mode_line));
7672 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
2f61d1d7 7673 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7674 && (change->argc < 2))
d76ed9a9 7675 {
7676 chanserv_conf.default_modes = *change;
7677 mod_chanmode_free(change);
7678 }
7679 free_string_list(chanserv_conf.set_shows);
7680 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7681 if(strlist)
7682 strlist = string_list_copy(strlist);
7683 else
7684 {
7685 static const char *list[] = {
7686 /* free form text */
7687 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7688 /* options based on user level */
4b6129c0 7689 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7690 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
d76ed9a9 7691 /* multiple choice options */
d3347099 7692 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
d76ed9a9 7693 /* binary options */
31f23f13 7694 "DynLimit", "NoDelete", "BanTimeout",
d76ed9a9 7695 /* delimiter */
7696 NULL
7697 };
7698 unsigned int ii;
7699 strlist = alloc_string_list(ArrayLength(list)-1);
7700 for(ii=0; list[ii]; ii++)
7701 string_list_append(strlist, strdup(list[ii]));
7702 }
7703 chanserv_conf.set_shows = strlist;
7704 /* We don't look things up now, in case the list refers to options
7705 * defined by modules initialized after this point. Just mark the
7706 * function list as invalid, so it will be initialized.
7707 */
7708 set_shows_list.used = 0;
7709 free_string_list(chanserv_conf.eightball);
7710 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7711 if(strlist)
7712 {
7713 strlist = string_list_copy(strlist);
7714 }
7715 else
7716 {
7717 strlist = alloc_string_list(4);
7718 string_list_append(strlist, strdup("Yes."));
7719 string_list_append(strlist, strdup("No."));
7720 string_list_append(strlist, strdup("Maybe so."));
7721 }
7722 chanserv_conf.eightball = strlist;
7723 free_string_list(chanserv_conf.old_ban_names);
7724 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7725 if(strlist)
7726 strlist = string_list_copy(strlist);
7727 else
7728 strlist = alloc_string_list(2);
7729 chanserv_conf.old_ban_names = strlist;
d76ed9a9 7730 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
1117fc5a 7731 off_channel = str ? atoi(str) : 0;
d76ed9a9 7732}
7733
7734static void
7735chanserv_note_type_read(const char *key, struct record_data *rd)
7736{
7737 dict_t obj;
7738 struct note_type *ntype;
7739 const char *str;
7740
7741 if(!(obj = GET_RECORD_OBJECT(rd)))
7742 {
7743 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7744 return;
7745 }
7746 if(!(ntype = chanserv_create_note_type(key)))
7747 {
7748 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7749 return;
7750 }
7751
7752 /* Figure out set access */
7753 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7754 {
7755 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7756 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7757 }
7758 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7759 {
7760 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7761 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7762 }
7763 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7764 {
7765 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7766 }
7767 else
7768 {
7769 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7770 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7771 ntype->set_access.min_opserv = 0;
7772 }
7773
7774 /* Figure out visibility */
7775 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7776 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7777 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7778 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7779 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7780 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7781 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7782 ntype->visible_type = NOTE_VIS_ALL;
7783 else
7784 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7785
7786 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7787 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7788}
7789
7790static void
7791user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7792{
7793 struct handle_info *handle;
7794 struct userData *uData;
b10abdb2 7795 char *seen, *inf, *flags, *expires;
d76ed9a9 7796 time_t last_seen;
7797 unsigned short access;
7798
7799 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7800 {
7801 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7802 return;
7803 }
7804
7805 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7806 if(access > UL_OWNER)
7807 {
7808 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7809 return;
7810 }
7811
7812 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7813 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7814 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7815 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
b10abdb2 7816 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
d76ed9a9 7817 handle = get_handle_info(key);
7818 if(!handle)
7819 {
7820 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7821 return;
7822 }
7823
7824 uData = add_channel_user(chan, handle, access, last_seen, inf);
7825 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
b10abdb2 7826 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
7827
7828 if((uData->flags & USER_SUSPENDED) && uData->expires)
7829 {
7830 if(uData->expires > now)
7831 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
7832 else
7833 uData->flags &= ~USER_SUSPENDED;
7834 }
c8ca69a0 7835
7836 /* Upgrade: set autoop to the inverse of noautoop */
7837 if(chanserv_read_version < 2)
7838 {
7839 /* if noautoop is true, set autoop false, and vice versa */
7840 if(uData->flags & USER_NOAUTO_OP)
7841 uData->flags = uData->flags & ~USER_AUTO_OP;
7842 else
7843 uData->flags = uData->flags | USER_AUTO_OP;
7844 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);
7845 }
7846
d76ed9a9 7847}
7848
7849static void
7850ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7851{
7852 struct banData *bData;
7853 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7854 time_t set_time, triggered_time, expires_time;
7855
7856 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7857 {
7858 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7859 return;
7860 }
7861
7862 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7863 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7864 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7865 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7866 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7867 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
0d16e639 7868 if (!reason || !owner)
7869 return;
d76ed9a9 7870
7871 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7872 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7873 if(s_expires)
7874 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7875 else if(s_duration)
7876 expires_time = set_time + atoi(s_duration);
7877 else
7878 expires_time = 0;
7879
ec1a68c8 7880 if(!reason || (expires_time && (expires_time < now)))
d76ed9a9 7881 return;
7882
7883 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7884}
7885
7886static struct suspended *
7887chanserv_read_suspended(dict_t obj)
7888{
7889 struct suspended *suspended = calloc(1, sizeof(*suspended));
7890 char *str;
7891 dict_t previous;
7892
7893 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7894 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7895 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7896 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7897 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7898 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7899 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7900 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7901 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7902 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7903 return suspended;
7904}
7905
82f37c08 7906static struct giveownership *
7907chanserv_read_giveownership(dict_t obj)
7908{
7909 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
7910 char *str;
7911 dict_t previous;
7912
7913 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
7914 giveownership->staff_issuer = str ? strdup(str) : NULL;
7915
7916 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
7917
7918 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
7919 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
7920
7921 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
7922 giveownership->reason = str ? strdup(str) : NULL;
7923 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7924 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7925
7926 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7927 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
7928 return giveownership;
7929}
7930
d76ed9a9 7931static int
7932chanserv_channel_read(const char *key, struct record_data *hir)
7933{
7934 struct suspended *suspended;
82f37c08 7935 struct giveownership *giveownership;
d76ed9a9 7936 struct mod_chanmode *modes;
7937 struct chanNode *cNode;
7938 struct chanData *cData;
7939 struct dict *channel, *obj;
7940 char *str, *argv[10];
7941 dict_iterator_t it;
7942 unsigned int argc;
7943
7944 channel = hir->d.object;
7945
7946 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7947 if(!str)
7948 str = "<unknown>";
2aef5f4b 7949 cNode = AddChannel(key, now, NULL, NULL, NULL);
d76ed9a9 7950 if(!cNode)
7951 {
7952 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7953 return 0;
7954 }
7955 cData = register_channel(cNode, str);
7956 if(!cData)
7957 {
7958 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7959 return 0;
7960 }
7961
7962 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7963 {
7964 enum levelOption lvlOpt;
7965 enum charOption chOpt;
7966
7967 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7968 cData->flags = atoi(str);
7969
7970 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7971 {
7972 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7973 if(str)
7974 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7975 else if(levelOptions[lvlOpt].old_flag)
7976 {
7977 if(cData->flags & levelOptions[lvlOpt].old_flag)
7978 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7979 else
7980 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7981 }
7982 }
7983
7984 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7985 {
7986 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7987 continue;
7988 cData->chOpts[chOpt] = str[0];
7989 }
7990 }
7991 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7992 {
7993 enum levelOption lvlOpt;
7994 enum charOption chOpt;
7995 unsigned int count;
7996
7997 cData->flags = base64toint(str, 5);
7998 count = strlen(str += 5);
7999 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8000 {
8001 unsigned short lvl;
8002 if(levelOptions[lvlOpt].old_flag)
8003 {
8004 if(cData->flags & levelOptions[lvlOpt].old_flag)
8005 lvl = levelOptions[lvlOpt].flag_value;
8006 else
8007 lvl = levelOptions[lvlOpt].default_value;
8008 }
8009 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8010 {
8011 case 'c': lvl = UL_COOWNER; break;
4048352e 8012 case 'm': lvl = UL_MANAGER; break;
d76ed9a9 8013 case 'n': lvl = UL_OWNER+1; break;
8014 case 'o': lvl = UL_OP; break;
8015 case 'p': lvl = UL_PEON; break;
55342ce8 8016 case 'h': lvl = UL_HALFOP; break;
d76ed9a9 8017 case 'w': lvl = UL_OWNER; break;
8018 default: lvl = 0; break;
8019 }
8020 cData->lvlOpts[lvlOpt] = lvl;
8021 }
8022 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8023 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8024 }
8025
8026 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8027 {
8028 suspended = chanserv_read_suspended(obj);
8029 cData->suspended = suspended;
8030 suspended->cData = cData;
8031 /* We could use suspended->expires and suspended->revoked to
8032 * set the CHANNEL_SUSPENDED flag, but we don't. */
8033 }
a32da4c7 8034 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
d76ed9a9 8035 {
8036 suspended = calloc(1, sizeof(*suspended));
8037 suspended->issued = 0;
8038 suspended->revoked = 0;
a32da4c7 8039 suspended->suspender = strdup(str);
d76ed9a9 8040 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8041 suspended->expires = str ? atoi(str) : 0;
d76ed9a9 8042 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8043 suspended->reason = strdup(str ? str : "No reason");
8044 suspended->previous = NULL;
8045 cData->suspended = suspended;
8046 suspended->cData = cData;
8047 }
8048 else
a32da4c7 8049 {
8050 cData->flags &= ~CHANNEL_SUSPENDED;
d76ed9a9 8051 suspended = NULL; /* to squelch a warning */
a32da4c7 8052 }
d76ed9a9 8053
8054 if(IsSuspended(cData)) {
8055 if(suspended->expires > now)
8056 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8057 else if(suspended->expires)
8058 cData->flags &= ~CHANNEL_SUSPENDED;
8059 }
8060
82f37c08 8061 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8062 {
8063 giveownership = chanserv_read_giveownership(obj);
8064 cData->giveownership = giveownership;
8065 }
8066
d76ed9a9 8067 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8068 struct mod_chanmode change;
8069 mod_chanmode_init(&change);
8070 change.argc = 1;
8071 change.args[0].mode = MODE_CHANOP;
a32da4c7 8072 change.args[0].u.member = AddChannelUser(chanserv, cNode);
d76ed9a9 8073 mod_chanmode_announce(chanserv, cNode, &change);
8074 }
8075
8076 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8077 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
8078 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8079 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
a32da4c7 8080 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8081 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
d76ed9a9 8082 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8083 cData->max = str ? atoi(str) : 0;
8084 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8085 cData->greeting = str ? strdup(str) : NULL;
8086 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8087 cData->user_greeting = str ? strdup(str) : NULL;
8088 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8089 cData->topic_mask = str ? strdup(str) : NULL;
8090 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8091 cData->topic = str ? strdup(str) : NULL;
8092
8093 if(!IsSuspended(cData)
8094 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8095 && (argc = split_line(str, 0, ArrayLength(argv), argv))
2f61d1d7 8096 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
d76ed9a9 8097 cData->modes = *modes;
1117fc5a 8098 if(off_channel > 0)
a32da4c7 8099 cData->modes.modes_set |= MODE_REGISTERED;
d76ed9a9 8100 if(cData->modes.argc > 1)
8101 cData->modes.argc = 1;
8102 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8103 mod_chanmode_free(modes);
8104 }
8105
8106 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8107 for(it = dict_first(obj); it; it = iter_next(it))
8108 user_read_helper(iter_key(it), iter_data(it), cData);
8109
8110 if(!cData->users && !IsProtected(cData))
8111 {
8112 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8113 unregister_channel(cData, "has empty user list.");
8114 return 0;
8115 }
8116
8117 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8118 for(it = dict_first(obj); it; it = iter_next(it))
8119 ban_read_helper(iter_key(it), iter_data(it), cData);
8120
8121 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8122 for(it = dict_first(obj); it; it = iter_next(it))
8123 {
8124 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8125 struct record_data *rd = iter_data(it);
8126 const char *note, *setter;
8127
8128 if(rd->type != RECDB_OBJECT)
8129 {
8130 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8131 }
8132 else if(!ntype)
8133 {
8134 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8135 }
8136 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8137 {
8138 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8139 }
8140 else
8141 {
8142 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8143 if(!setter) setter = "<unknown>";
8144 chanserv_add_channel_note(cData, ntype, setter, note);
8145 }
8146 }
8147
8148 return 0;
8149}
8150
8151static void
8152chanserv_dnr_read(const char *key, struct record_data *hir)
8153{
8154 const char *setter, *reason, *str;
8155 struct do_not_register *dnr;
8156
8157 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8158 if(!setter)
8159 {
8160 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8161 return;
8162 }
8163 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8164 if(!reason)
8165 {
8166 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8167 return;
8168 }
8169 dnr = chanserv_add_dnr(key, setter, reason);
8170 if(!dnr)
8171 return;
8172 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8173 if(str)
8174 dnr->set = atoi(str);
8175 else
8176 dnr->set = 0;
8177}
8178
c8ca69a0 8179static void
8180chanserv_version_read(struct dict *section)
8181{
8182 /* global var.. */
8183 char *str;
8184 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
8185 if(str)
8186 chanserv_read_version = atoi(str);
8187 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
8188}
8189
d76ed9a9 8190static int
8191chanserv_saxdb_read(struct dict *database)
8192{
8193 struct dict *section;
8194 dict_iterator_t it;
8195
c8ca69a0 8196 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
8197 chanserv_version_read(section);
8198
d76ed9a9 8199 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8200 for(it = dict_first(section); it; it = iter_next(it))
8201 chanserv_note_type_read(iter_key(it), iter_data(it));
8202
8203 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8204 for(it = dict_first(section); it; it = iter_next(it))
8205 chanserv_channel_read(iter_key(it), iter_data(it));
8206
8207 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8208 for(it = dict_first(section); it; it = iter_next(it))
8209 chanserv_dnr_read(iter_key(it), iter_data(it));
8210
8211 return 0;
8212}
8213
8214static int
8215chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8216{
8217 int high_present = 0;
8218 saxdb_start_record(ctx, KEY_USERS, 1);
8219 for(; uData; uData = uData->next)
8220 {
8221 if((uData->access >= UL_PRESENT) && uData->present)
8222 high_present = 1;
8223 saxdb_start_record(ctx, uData->handle->handle, 0);
8224 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8225 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8226 if(uData->flags)
8227 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
b10abdb2 8228 if(uData->expires)
8229 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
d76ed9a9 8230 if(uData->info)
8231 saxdb_write_string(ctx, KEY_INFO, uData->info);
8232 saxdb_end_record(ctx);
8233 }
8234 saxdb_end_record(ctx);
8235 return high_present;
8236}
8237
8238static void
8239chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8240{
8241 if(!bData)
8242 return;
8243 saxdb_start_record(ctx, KEY_BANS, 1);
8244 for(; bData; bData = bData->next)
8245 {
8246 saxdb_start_record(ctx, bData->mask, 0);
8247 saxdb_write_int(ctx, KEY_SET, bData->set);
8248 if(bData->triggered)
8249 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8250 if(bData->expires)
8251 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8252 if(bData->owner[0])
8253 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8254 if(bData->reason)
8255 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8256 saxdb_end_record(ctx);
8257 }
8258 saxdb_end_record(ctx);
8259}
8260
8261static void
8262chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8263{
8264 saxdb_start_record(ctx, name, 0);
8265 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8266 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8267 if(susp->issued)
8268 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8269 if(susp->expires)
8270 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8271 if(susp->revoked)
8272 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8273 if(susp->previous)
8274 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8275 saxdb_end_record(ctx);
8276}
8277
82f37c08 8278static void
8279chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8280{
8281 saxdb_start_record(ctx, name, 0);
8282 if(giveownership->staff_issuer)
8283 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8284 if(giveownership->old_owner)
8285 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8286 if(giveownership->target)
8287 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8288 if(giveownership->target_access)
8289 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8290 if(giveownership->reason)
8291 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8292 if(giveownership->issued)
8293 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8294 if(giveownership->previous)
8295 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8296 saxdb_end_record(ctx);
8297}
8298
d76ed9a9 8299static void
8300chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8301{
8302 char buf[MAXLEN];
8303 int high_present;
8304 enum levelOption lvlOpt;
8305 enum charOption chOpt;
8306
8307 saxdb_start_record(ctx, channel->channel->name, 1);
8308
8309 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8310 saxdb_write_int(ctx, KEY_MAX, channel->max);
8311 if(channel->topic)
8312 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8313 if(channel->registrar)
8314 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8315 if(channel->greeting)
8316 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8317 if(channel->user_greeting)
8318 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8319 if(channel->topic_mask)
8320 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8321 if(channel->suspended)
8322 chanserv_write_suspended(ctx, "suspended", channel->suspended);
82f37c08 8323 if(channel->giveownership)
8324 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
d76ed9a9 8325
8326 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8327 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8328 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8329 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8330 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8331 {
8332 buf[0] = channel->chOpts[chOpt];
8333 buf[1] = '\0';
8334 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8335 }
8336 saxdb_end_record(ctx);
8337
8338 if(channel->modes.modes_set || channel->modes.modes_clear)
8339 {
8340 mod_chanmode_format(&channel->modes, buf);
8341 saxdb_write_string(ctx, KEY_MODES, buf);
8342 }
8343
8344 high_present = chanserv_write_users(ctx, channel->users);
8345 chanserv_write_bans(ctx, channel->bans);
8346
8347 if(dict_size(channel->notes))
8348 {
8349 dict_iterator_t it;
8350
8351 saxdb_start_record(ctx, KEY_NOTES, 1);
8352 for(it = dict_first(channel->notes); it; it = iter_next(it))
8353 {
8354 struct note *note = iter_data(it);
8355 saxdb_start_record(ctx, iter_key(it), 0);
8356 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8357 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8358 saxdb_end_record(ctx);
8359 }
8360 saxdb_end_record(ctx);
8361 }
8362
a32da4c7 8363 if(channel->ownerTransfer)
8364 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
d76ed9a9 8365 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8366 saxdb_end_record(ctx);
8367}
8368
8369static void
8370chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8371{
8372 const char *str;
8373
8374 saxdb_start_record(ctx, ntype->name, 0);
8375 switch(ntype->set_access_type)
8376 {
338a82b5 8377 case NOTE_SET_CHANNEL_ACCESS:
8378 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8379 break;
8380 case NOTE_SET_CHANNEL_SETTER:
8381 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8382 break;
8383 case NOTE_SET_PRIVILEGED: default:
8384 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8385 break;
d76ed9a9 8386 }
8387 switch(ntype->visible_type)
8388 {
338a82b5 8389 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8390 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8391 case NOTE_VIS_PRIVILEGED:
8392 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
d76ed9a9 8393 }
8394 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8395 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8396 saxdb_end_record(ctx);
8397}
8398
8399static void
8400write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8401{
8402 struct do_not_register *dnr;
8403 dict_iterator_t it;
8404
8405 for(it = dict_first(dnrs); it; it = iter_next(it))
8406 {
8407 dnr = iter_data(it);
8408 saxdb_start_record(ctx, dnr->chan_name, 0);
8409 if(dnr->set)
8410 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8411 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8412 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8413 saxdb_end_record(ctx);
8414 }
8415}
8416
8417static int
8418chanserv_saxdb_write(struct saxdb_context *ctx)
8419{
8420 dict_iterator_t it;
8421 struct chanData *channel;
8422
c8ca69a0 8423 /* Version Control*/
8424 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
8425 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
8426 saxdb_end_record(ctx);
8427
d76ed9a9 8428 /* Notes */
8429 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8430 for(it = dict_first(note_types); it; it = iter_next(it))
8431 chanserv_write_note_type(ctx, iter_data(it));
8432 saxdb_end_record(ctx);
8433
8434 /* DNRs */
8435 saxdb_start_record(ctx, KEY_DNR, 1);
8436 write_dnrs_helper(ctx, handle_dnrs);
8437 write_dnrs_helper(ctx, plain_dnrs);
8438 write_dnrs_helper(ctx, mask_dnrs);
8439 saxdb_end_record(ctx);
8440
8441 /* Channels */
8442 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8443 for(channel = channelList; channel; channel = channel->next)
8444 chanserv_write_channel(ctx, channel);
8445 saxdb_end_record(ctx);
8446
8447 return 0;
8448}
8449
8450static void
8451chanserv_db_cleanup(void) {
8452 unsigned int ii;
8453 unreg_part_func(handle_part);
8454 while(channelList)
8455 unregister_channel(channelList, "terminating.");
8456 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8457 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8458 free(chanserv_conf.support_channels.list);
8459 dict_delete(handle_dnrs);
8460 dict_delete(plain_dnrs);
8461 dict_delete(mask_dnrs);
8462 dict_delete(note_types);
8463 free_string_list(chanserv_conf.eightball);
8464 free_string_list(chanserv_conf.old_ban_names);
8465 free_string_list(chanserv_conf.set_shows);
8466 free(set_shows_list.list);
8467 free(uset_shows_list.list);
8468 while(helperList)
8469 {
8470 struct userData *helper = helperList;
8471 helperList = helperList->next;
8472 free(helper);
8473 }
8474}
8475
8476#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8477#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8478#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8479
8480void
8481init_chanserv(const char *nick)
8482{
7637f48f 8483 struct chanNode *chan;
8484 unsigned int i;
d76ed9a9 8485 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8486 conf_register_reload(chanserv_conf_read);
8487
8488 reg_server_link_func(handle_server_link);
8489
8490 reg_new_channel_func(handle_new_channel);
8491 reg_join_func(handle_join);
8492 reg_part_func(handle_part);
8493 reg_kick_func(handle_kick);
8494 reg_topic_func(handle_topic);
8495 reg_mode_change_func(handle_mode);
8496 reg_nick_change_func(handle_nick_change);
8497
8498 reg_auth_func(handle_auth);
8499 reg_handle_rename_func(handle_rename);
8500 reg_unreg_func(handle_unreg);
8501
8502 handle_dnrs = dict_new();
8503 dict_set_free_data(handle_dnrs, free);
8504 plain_dnrs = dict_new();
8505 dict_set_free_data(plain_dnrs, free);
8506 mask_dnrs = dict_new();
8507 dict_set_free_data(mask_dnrs, free);
8508
8509 reg_svccmd_unbind_func(handle_svccmd_unbind);
8510 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8511 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8512 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8513 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8514 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8515 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8516 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8517 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8518 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8519
ac3bdc8d 8520 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8521
d76ed9a9 8522 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8523 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8524
4048352e 8525 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8526 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8527 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8528 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8529 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8530
8531 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8532 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
4048352e 8533 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8534 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8535 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
55342ce8 8536 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8537
4048352e 8538 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8539 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
4048352e 8540 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8541 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8542
8543 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8544 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8545 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8546 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
55342ce8 8547 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
d76ed9a9 8548 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8549 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
55342ce8 8550 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
d76ed9a9 8551 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8552 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8553
c8273589 8554 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8555 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8556 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8557 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
8558 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
8559 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
d76ed9a9 8560 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
c8273589 8561 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
d76ed9a9 8562 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8563 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
4048352e 8564 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
d76ed9a9 8565 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
4048352e 8566 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8567 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8568
c8273589 8569 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
23475fc6 8570 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
c8273589 8571 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8572 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8573
8574 /* if you change dellamer access, see also places
8575 * like unbanme which have manager hardcoded. */
8576 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
d76ed9a9 8577 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8578
c8273589 8579 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8580
d76ed9a9 8581 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8582
8583 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8584 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8585 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8586 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8587 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8588 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8589 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
55342ce8 8590 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
d76ed9a9 8591 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8592 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8593 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8594 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8595
8596 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8597 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8598
8599 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8600 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8601 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8602 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8603
8604 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8605 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8606 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8607 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8608 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8609
8610 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8611 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8612 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8613 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8614 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8615 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
b8daef0f 8616 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
33741441 8617 DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
d76ed9a9 8618
8619 /* Channel options */
8620 DEFINE_CHANNEL_OPTION(defaulttopic);
8621 DEFINE_CHANNEL_OPTION(topicmask);
8622 DEFINE_CHANNEL_OPTION(greeting);
8623 DEFINE_CHANNEL_OPTION(usergreeting);
8624 DEFINE_CHANNEL_OPTION(modes);
8625 DEFINE_CHANNEL_OPTION(enfops);
55342ce8 8626 DEFINE_CHANNEL_OPTION(enfhalfops);
4b6129c0 8627 DEFINE_CHANNEL_OPTION(automode);
d76ed9a9 8628 DEFINE_CHANNEL_OPTION(protect);
8629 DEFINE_CHANNEL_OPTION(enfmodes);
8630 DEFINE_CHANNEL_OPTION(enftopic);
8631 DEFINE_CHANNEL_OPTION(pubcmd);
d76ed9a9 8632 DEFINE_CHANNEL_OPTION(userinfo);
8633 DEFINE_CHANNEL_OPTION(dynlimit);
8634 DEFINE_CHANNEL_OPTION(topicsnarf);
8635 DEFINE_CHANNEL_OPTION(nodelete);
8636 DEFINE_CHANNEL_OPTION(toys);
8637 DEFINE_CHANNEL_OPTION(setters);
8638 DEFINE_CHANNEL_OPTION(topicrefresh);
7637f48f 8639 DEFINE_CHANNEL_OPTION(resync);
d76ed9a9 8640 DEFINE_CHANNEL_OPTION(ctcpreaction);
31f23f13 8641 DEFINE_CHANNEL_OPTION(bantimeout);
d76ed9a9 8642 DEFINE_CHANNEL_OPTION(inviteme);
1117fc5a 8643 if(off_channel > 1)
d76ed9a9 8644 DEFINE_CHANNEL_OPTION(offchannel);
8645 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8646
8647 /* Alias set topic to set defaulttopic for compatibility. */
8648 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8649
8650 /* User options */
d76ed9a9 8651 DEFINE_USER_OPTION(autoinvite);
cd25f2e9 8652 DEFINE_USER_OPTION(autojoin);
d76ed9a9 8653 DEFINE_USER_OPTION(info);
c8ca69a0 8654 DEFINE_USER_OPTION(autoop);
d76ed9a9 8655
8656 /* Alias uset autovoice to uset autoop. */
c8ca69a0 8657 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
d76ed9a9 8658
8659 note_types = dict_new();
8660 dict_set_free_data(note_types, chanserv_deref_note_type);
8661 if(nick)
8662 {
a32da4c7 8663 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8664 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
d76ed9a9 8665 service_register(chanserv)->trigger = '!';
8666 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8667 }
c8ca69a0 8668
d76ed9a9 8669 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8670
8671 if(chanserv_conf.channel_expire_frequency)
8672 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8673
31f23f13 8674 if(chanserv_conf.ban_timeout_frequency)
8675 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
8676
d76ed9a9 8677 if(chanserv_conf.refresh_period)
8678 {
8679 time_t next_refresh;
8680 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8681 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7637f48f 8682 timeq_add(next_refresh, chanserv_auto_resync, NULL);
8683 }
8684
8685 if (autojoin_channels && chanserv) {
8686 for (i = 0; i < autojoin_channels->used; i++) {
8687 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
8688 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
8689 }
d76ed9a9 8690 }
1117fc5a 8691
d76ed9a9 8692 reg_exit_func(chanserv_db_cleanup);
8693 message_register_table(msgtab);
8694}