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