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