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