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