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