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