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