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