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