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