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