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