]> jfr.im git - irc/evilnet/x3.git/blob - src/chanserv.c
Added ignore_csuspend to *ChanServ.csuspend and *ChanServ.cunsuspend as a default...
[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 %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) || IsHideOper(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 abusednick[NICKLEN] = "";
7818 int abusednum = 1 + (int) (10000.0 * (rand() / (RAND_MAX + 1.0)));
7819 struct userNode *clone;
7820
7821 oldnick = strdup(user->nick);
7822 oldident = strdup(user->ident);
7823
7824 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7825 while (1) {
7826 snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
7827 log_module(MAIN_LOG, LOG_DEBUG, "Abused Nick: %s, Client Nick: %s", abusednick, user->nick);
7828 if(!GetUserH(abusednick))
7829 break;
7830 }
7831
7832 SVSNickChange(user, abusednick);
7833 irc_svsnick(chanserv, user, abusednick);
7834 clone = AddLocalUser(oldnick, oldident, "abused.by.wheel.of.misfortune", "I got abused by the wheel of misfortune :D", "+i");
7835 timeq_add(now + 300, chanserv_remove_abuse, clone->nick);
7836 }
7837 /* kill */
7838 else if (!strcasecmp(wheel, "kill")) {
7839 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KILL");
7840
7841 DelUser(user, chanserv, 1, "Reward for spinning the wheel of misfortune!");
7842 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
7843 }
7844 /* service ignore */
7845 else if (!strcasecmp(wheel, "svsignore")) {
7846 int gagged, ignoretime = 0;
7847 char target[HOSTLEN + 13];
7848
7849 if(IsOper(user)) {
7850 /* we cant gag opers, so just verbally abuse them */
7851 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE_OPER");
7852 return 1;
7853 }
7854 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE");
7855
7856 strcpy(target, "*!*@");
7857 strcat(target, user->hostname);
7858 ignoretime = now + (1 + rand() % 120);
7859
7860 gagged = gag_create(target, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime);
7861 }
7862 /* kick and ban from each channel your in */
7863 else if (!strcasecmp(wheel, "kickbanall")) {
7864 unsigned int count, n;
7865 struct modeNode *mn;
7866 //char ban[HOSTLEN + 1];
7867
7868 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKBANALL");
7869
7870 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
7871 for (n=count=0; n<user->channels.used; n++) {
7872 struct mod_chanmode *change;
7873 /* struct banData *bData; */
7874 unsigned int exists;
7875 /* int duration = 300; */
7876 char *ban;
7877
7878 ban = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT|GENMASK_USENICK);
7879
7880 log_module(MAIN_LOG, LOG_DEBUG, "Generated ban %s", ban);
7881 mn = user->channels.list[n];
7882 if(mn->channel->banlist.used >= MAXBANS) {
7883 reply("CSMSG_BANLIST_FULL", mn->channel->name);
7884 free(ban);
7885 continue;
7886 }
7887
7888 /* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
7889
7890 change = mod_chanmode_alloc(1);
7891 change->args[0].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
7892 change->args[0].u.member = GetUserMode(mn->channel, user);
7893 change->argc = 1;
7894
7895 mod_chanmode_announce(chanserv, mn->channel, change);
7896 mod_chanmode_free(change);
7897
7898 exists = ChannelBanExists(mn->channel, ban);
7899 if(!exists) {
7900 change = mod_chanmode_alloc(1);
7901 change->args[0].mode = MODE_BAN;
7902 change->args[0].u.hostmask = ban;
7903 change->argc = 1;
7904 mod_chanmode_announce(chanserv, mn->channel, change);
7905 mod_chanmode_free(change);
7906 }
7907
7908 if(exists) {
7909 reply("CSMSG_REDUNDANT_BAN", ban, mn->channel->name);
7910 free(ban);
7911 }
7912
7913 irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
7914 }
7915 }
7916 else {
7917 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_UNKNOWN", wheel);
7918 }
7919
7920 return 1;
7921 }
7922
7923 #ifdef lame8ball
7924 static CHANSERV_FUNC(cmd_8ball)
7925 {
7926 unsigned int i, j, accum;
7927 const char *resp;
7928
7929 REQUIRE_PARAMS(2);
7930 accum = 0;
7931 for(i=1; i<argc; i++)
7932 for(j=0; argv[i][j]; j++)
7933 accum = (accum << 5) - accum + toupper(argv[i][j]);
7934 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7935 if(channel)
7936 {
7937 char response[MAXLEN];
7938 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
7939 irc_privmsg(cmd->parent->bot, channel->name, response);
7940 }
7941 else
7942 send_message_type(4, user, cmd->parent->bot, "%s", resp);
7943 return 1;
7944 }
7945
7946 #else /* Use cool 8ball instead */
7947
7948 void eightball(char *outcome, int method, unsigned int seed)
7949 {
7950 int answer = 0;
7951
7952 #define NUMOFCOLORS 18
7953 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
7954 "white", "black", "grey", "brown",
7955 "yellow", "pink", "purple", "orange", "teal", "burgandy",
7956 "fuchsia","turquoise","magenta", "cyan"};
7957 #define NUMOFLOCATIONS 50
7958 char balllocations[50][55] = {
7959 "Locke's house", "Oregon", "California", "Indiana", "Canada",
7960 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
7961 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
7962 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
7963 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
7964 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
7965 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
7966 "your bra", "your hair", "your bed", "the couch", "the wall",
7967 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
7968 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
7969 #define NUMOFPREPS 15
7970 char ballpreps[50][50] = {
7971 "Near", "Somewhere near", "In", "In", "In",
7972 "In", "Hiding in", "Under", "Next to", "Over",
7973 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
7974 #define NUMOFNUMS 34
7975 char ballnums[50][50] = {
7976 "A hundred", "A thousand", "A few", "42",
7977 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
7978 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7979 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
7980 };
7981 #define NUMOFMULTS 8
7982 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
7983
7984 /* Method:
7985 * 0: normal (Not used in x3)
7986 * 1: color
7987 * 2: where is
7988 * 3: how many
7989 */
7990
7991 srand(seed);
7992 if (method == 1) /* A Color */
7993 {
7994 char tmp[MAXLEN];
7995
7996 answer = (rand() % 12); /* Make sure this is the # of entries */
7997 switch(answer)
7998 {
7999 case 0: strcpy(tmp, "Very bright %s, I'd say.");
8000 break;
8001 case 1: strcpy(tmp, "Sort of a light %s color.");
8002 break;
8003 case 2: strcpy(tmp, "Dark and dreary %s.");
8004 break;
8005 case 3: strcpy(tmp, "Quite a pale shade of %s.");
8006 break;
8007 case 4: strcpy(tmp, "A gross kind of mucky %s.");
8008 break;
8009 case 5: strcpy(tmp, "Brilliant whiteish %s.");
8010 break;
8011 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
8012 break;
8013 case 10: strcpy(tmp, "Solid %s.");
8014 break;
8015 case 11: strcpy(tmp, "Transparent %s.");
8016 break;
8017 default: strcpy(outcome, "An invalid random number was generated.");
8018 return;
8019 }
8020 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
8021 return;
8022 }
8023 else if (method == 2) /* Location */
8024 {
8025 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
8026 }
8027 else if (method == 3) /* Number of ___ */
8028 {
8029 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
8030 }
8031 else
8032 {
8033 //Debug(DBGWARNING, "Error in 8ball.");
8034 }
8035 return;
8036 }
8037
8038 static CHANSERV_FUNC(cmd_8ball)
8039 {
8040 char *word1, *word2, *word3;
8041 static char eb[MAXLEN];
8042 unsigned int accum, i, j;
8043
8044 REQUIRE_PARAMS(2);
8045 accum = 0;
8046 for(i=1; i<argc; i++)
8047 for(j=0; argv[i][j]; j++)
8048 accum = (accum << 5) - accum + toupper(argv[i][j]);
8049
8050 accum += time(NULL)/3600;
8051 word1 = argv[1];
8052 word2 = argc>2?argv[2]:"";
8053 word3 = argc>3?argv[3]:"";
8054
8055 /*** COLOR *****/
8056 if((word2) && strcasecmp(word1, "what") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
8057 eightball(eb, 1, accum);
8058 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
8059 eightball(eb, 1, accum);
8060 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
8061 eightball(eb, 1, accum);
8062 /*** LOCATION *****/
8063 else if(
8064 (
8065 word2 &&
8066 (
8067 (strcasecmp(word1, "where") == 0) &&
8068 (strcasecmp(word2, "is") == 0)
8069 )
8070 ) ||
8071 (
8072 strcasecmp(word1, "where's") == 0
8073 )
8074 )
8075 eightball(eb, 2, accum);
8076 /*** NUMBER *****/
8077 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
8078 eightball(eb, 3, accum);
8079 /*** GENERIC *****/
8080 else
8081 {
8082 /* Generic 8ball question.. so pull from x3.conf srvx style */
8083 const char *resp;
8084
8085 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
8086 if(channel)
8087 {
8088 char response[MAXLEN];
8089 sprintf(response, "\002%s\002: %s", user->nick, resp);
8090 irc_privmsg(cmd->parent->bot, channel->name, response);
8091 }
8092 else
8093 send_message_type(4, user, cmd->parent->bot, "%s", resp);
8094 return 1;
8095 }
8096
8097 if(channel)
8098 {
8099 char response[MAXLEN];
8100 sprintf(response, "\002%s\002: %s", user->nick, eb);
8101 irc_privmsg(cmd->parent->bot, channel->name, response);
8102 }
8103 else
8104 send_message_type(4, user, cmd->parent->bot, "%s", eb);
8105 return 1;
8106 }
8107 #endif
8108
8109 static CHANSERV_FUNC(cmd_d)
8110 {
8111 unsigned long sides, count, modifier, ii, total;
8112 char response[MAXLEN], *sep;
8113 const char *fmt;
8114
8115 REQUIRE_PARAMS(2);
8116 if((count = strtoul(argv[1], &sep, 10)) < 1)
8117 goto no_dice;
8118 if(sep[0] == 0)
8119 {
8120 if(count == 1)
8121 goto no_dice;
8122 sides = count;
8123 count = 1;
8124 modifier = 0;
8125 }
8126 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
8127 && (sides = strtoul(sep+1, &sep, 10)) > 1)
8128 {
8129 if(sep[0] == 0)
8130 modifier = 0;
8131 else if((sep[0] == '-') && isdigit(sep[1]))
8132 modifier = strtoul(sep, NULL, 10);
8133 else if((sep[0] == '+') && isdigit(sep[1]))
8134 modifier = strtoul(sep+1, NULL, 10);
8135 else
8136 goto no_dice;
8137 }
8138 else
8139 {
8140 no_dice:
8141 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
8142 return 0;
8143 }
8144 if(count > 10)
8145 {
8146 reply("CSMSG_BAD_DICE_COUNT", count, 10);
8147 return 0;
8148 }
8149 for(total = ii = 0; ii < count; ++ii)
8150 total += (rand() % sides) + 1;
8151 total += modifier;
8152
8153 if((count > 1) || modifier)
8154 {
8155 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
8156 sprintf(response, fmt, total, count, sides, modifier);
8157 }
8158 else
8159 {
8160 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
8161 sprintf(response, fmt, total, sides);
8162 }
8163 if(channel)
8164 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
8165 else
8166 send_message_type(4, user, cmd->parent->bot, "%s", response);
8167 return 1;
8168 }
8169
8170 static CHANSERV_FUNC(cmd_huggle)
8171 {
8172 /* CTCP must be via PRIVMSG, never notice */
8173 if(channel)
8174 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
8175 else
8176 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
8177 return 1;
8178 }
8179
8180 static CHANSERV_FUNC(cmd_calc)
8181 {
8182 char response[MAXLEN];
8183
8184 REQUIRE_PARAMS(2);
8185 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
8186
8187 if(channel)
8188 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
8189 else
8190 send_message_type(4, user, cmd->parent->bot, "%s", response);
8191 return 1;
8192 }
8193
8194 static CHANSERV_FUNC(cmd_reply)
8195 {
8196
8197 REQUIRE_PARAMS(2);
8198 unsplit_string(argv + 1, argc - 1, NULL);
8199
8200 if(channel)
8201 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, unsplit_string(argv + 1, argc - 1, NULL));
8202 else
8203 send_message_type(4, user, cmd->parent->bot, "%s", unsplit_string(argv + 1, argc - 1, NULL));
8204 return 1;
8205 }
8206
8207 static void
8208 chanserv_adjust_limit(void *data)
8209 {
8210 struct mod_chanmode change;
8211 struct chanData *cData = data;
8212 struct chanNode *channel = cData->channel;
8213 unsigned int limit;
8214
8215 if(IsSuspended(cData))
8216 return;
8217
8218 cData->limitAdjusted = now;
8219 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
8220 if(cData->modes.modes_set & MODE_LIMIT)
8221 {
8222 if(limit > cData->modes.new_limit)
8223 limit = cData->modes.new_limit;
8224 else if(limit == cData->modes.new_limit)
8225 return;
8226 }
8227
8228 mod_chanmode_init(&change);
8229 change.modes_set = MODE_LIMIT;
8230 change.new_limit = limit;
8231 mod_chanmode_announce(chanserv, channel, &change);
8232 }
8233
8234 static void
8235 handle_new_channel(struct chanNode *channel, UNUSED_ARG(void *extra))
8236 {
8237 struct chanData *cData;
8238
8239 if(!(cData = channel->channel_info))
8240 return;
8241
8242 if(cData->modes.modes_set || cData->modes.modes_clear)
8243 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
8244
8245 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
8246 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
8247 }
8248
8249 int
8250 trace_check_bans(struct userNode *user, struct chanNode *chan)
8251 {
8252 struct banData *bData;
8253 struct mod_chanmode *change;
8254
8255 change = find_matching_bans(&chan->banlist, user, NULL);
8256 if (change)
8257 return 1;
8258
8259 /* lamer list */
8260 if (chan->channel_info) {
8261 for(bData = chan->channel_info->bans; bData; bData = bData->next) {
8262
8263 if(!user_matches_glob(user, bData->mask, MATCH_USENICK, 0))
8264 continue;
8265
8266 if(bData)
8267 return 1;
8268 }
8269 }
8270
8271 return 0;
8272 }
8273
8274 int
8275 check_bans(struct userNode *user, const char *channel)
8276 {
8277 struct chanNode *chan;
8278 struct mod_chanmode change;
8279 struct chanData *cData;
8280 struct banData *bData;
8281
8282 if (!(chan = GetChannel(channel)))
8283 return 0;
8284
8285 if(!(cData = chan->channel_info))
8286 return 0;
8287
8288 mod_chanmode_init(&change);
8289 change.argc = 1;
8290
8291 if(chan->banlist.used < MAXBANS)
8292 {
8293 /* Not joining through a ban. */
8294 for(bData = cData->bans;
8295 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK, 0);
8296 bData = bData->next);
8297
8298 if(bData)
8299 {
8300 char kick_reason[MAXLEN];
8301 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8302
8303 bData->triggered = now;
8304 if(bData != cData->bans)
8305 {
8306 /* Shuffle the ban to the head of the list. */
8307 if(bData->next)
8308 bData->next->prev = bData->prev;
8309 if(bData->prev)
8310 bData->prev->next = bData->next;
8311
8312 bData->prev = NULL;
8313 bData->next = cData->bans;
8314
8315 if(cData->bans)
8316 cData->bans->prev = bData;
8317
8318 cData->bans = bData;
8319 }
8320
8321 change.args[0].mode = MODE_BAN;
8322 change.args[0].u.hostmask = bData->mask;
8323 mod_chanmode_announce(chanserv, chan, &change);
8324 KickChannelUser(user, chan, chanserv, kick_reason);
8325 return 1;
8326 }
8327 }
8328 return 0;
8329 }
8330
8331 int
8332 channel_user_is_exempt(struct userNode *user, struct chanNode *channel)
8333 {
8334 unsigned int ii;
8335 for(ii = 0; ii < channel->exemptlist.used; ii++)
8336 {
8337 if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK, 0))
8338 return true;
8339 }
8340 return false;
8341 }
8342
8343
8344 /* Welcome to my worst nightmare. Warning: Read (or modify)
8345 the code below at your own risk. */
8346 static int
8347 handle_join(struct modeNode *mNode, UNUSED_ARG(void *extra))
8348 {
8349 struct mod_chanmode change;
8350 struct userNode *user = mNode->user;
8351 struct chanNode *channel = mNode->channel;
8352 struct chanData *cData;
8353 struct userData *uData = NULL;
8354 struct banData *bData;
8355 struct handle_info *handle;
8356 unsigned int modes = 0, info = 0;
8357 char *greeting;
8358
8359 if(IsLocal(user) || !channel || !channel->channel_info || IsSuspended(channel->channel_info))
8360 return 0;
8361
8362 cData = channel->channel_info;
8363 if(channel->members.used > cData->max)
8364 cData->max = channel->members.used;
8365
8366 #ifdef notdef
8367 /* Check for bans. If they're joining through a ban, one of two
8368 * cases applies:
8369 * 1: Join during a netburst, by riding the break. Kick them
8370 * unless they have ops or voice in the channel.
8371 * 2: They're allowed to join through the ban (an invite in
8372 * ircu2.10, or a +e on Hybrid, or something).
8373 * If they're not joining through a ban, and the banlist is not
8374 * full, see if they're on the banlist for the channel. If so,
8375 * kickban them.
8376 */
8377 if(user->uplink->burst && !mNode->modes)
8378 {
8379 unsigned int ii;
8380 for(ii = 0; ii < channel->banlist.used; ii++)
8381 {
8382 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK, 0))
8383 {
8384 /* Riding a netburst. Naughty. */
8385 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
8386 return 1;
8387 }
8388 }
8389 }
8390 #endif
8391
8392 if(user->handle_info)
8393 {
8394 handle = user->handle_info;
8395 if(handle)
8396 {
8397 uData = GetTrueChannelAccess(cData, handle);
8398 }
8399 }
8400
8401
8402 mod_chanmode_init(&change);
8403 change.argc = 1;
8404
8405 /* TODO: maybe only people above inviteme level? -Rubin */
8406 /* We don't kick people with access */
8407 if(!uData && !channel_user_is_exempt(user, channel))
8408 {
8409 if(channel->banlist.used < MAXBANS)
8410 {
8411 /* Not joining through a ban. */
8412 for(bData = cData->bans;
8413 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK, 0);
8414 bData = bData->next);
8415
8416 if(bData)
8417 {
8418 char kick_reason[MAXLEN];
8419 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8420
8421 bData->triggered = now;
8422 if(bData != cData->bans)
8423 {
8424 /* Shuffle the ban to the head of the list. */
8425 if(bData->next)
8426 bData->next->prev = bData->prev;
8427 if(bData->prev)
8428 bData->prev->next = bData->next;
8429
8430 bData->prev = NULL;
8431 bData->next = cData->bans;
8432
8433 if(cData->bans)
8434 cData->bans->prev = bData;
8435 cData->bans = bData;
8436 }
8437
8438 change.args[0].mode = MODE_BAN;
8439 change.args[0].u.hostmask = bData->mask;
8440 mod_chanmode_announce(chanserv, channel, &change);
8441 KickChannelUser(user, channel, chanserv, kick_reason);
8442 return 1;
8443 }
8444 }
8445 }
8446
8447 /* ChanServ will not modify the limits in join-flooded channels.
8448 It will also skip DynLimit processing when the user (or srvx)
8449 is bursting in, because there are likely more incoming. */
8450 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
8451 && !user->uplink->burst
8452 && !channel->join_flooded
8453 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
8454 {
8455 /* The user count has begun "bumping" into the channel limit,
8456 so set a timer to raise the limit a bit. Any previous
8457 timers are removed so three incoming users within the delay
8458 results in one limit change, not three. */
8459
8460 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8461 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8462 }
8463
8464 /* Give automodes exept during join-floods */
8465 if(!channel->join_flooded)
8466 {
8467 if(cData->chOpts[chAutomode] == 'v')
8468 modes |= MODE_VOICE;
8469 else if(cData->chOpts[chAutomode] == 'h')
8470 modes |= MODE_HALFOP;
8471 else if(cData->chOpts[chAutomode] == 'o')
8472 modes |= MODE_CHANOP;
8473 }
8474
8475 greeting = cData->greeting;
8476 if(user->handle_info)
8477 {
8478 /* handle = user->handle_info; */
8479
8480 if(IsHelper(user) && !IsHelping(user))
8481 {
8482 unsigned int ii;
8483 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8484 {
8485 if(channel == chanserv_conf.support_channels.list[ii])
8486 {
8487 HANDLE_SET_FLAG(user->handle_info, HELPING);
8488 break;
8489 }
8490 }
8491 }
8492
8493 /* uData = GetTrueChannelAccess(cData, handle); */
8494 if(uData && !IsUserSuspended(uData))
8495 {
8496 /* non users getting automodes are handled above. */
8497 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
8498 {
8499 /* just op everyone with access */
8500 if(uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
8501 modes |= MODE_VOICE;
8502 /* or do their access level */
8503 else if(uData->access >= UL_OP )
8504 modes |= MODE_CHANOP;
8505 else if(uData->access >= UL_HALFOP )
8506 modes |= MODE_HALFOP;
8507 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
8508 modes |= MODE_VOICE;
8509 }
8510 if(uData->access >= UL_PRESENT)
8511 cData->visited = now;
8512 if(cData->user_greeting)
8513 greeting = cData->user_greeting;
8514 if(uData->info
8515 && (uData->access >= cData->lvlOpts[lvlUserInfo])
8516 && ((now - uData->seen) >= chanserv_conf.info_delay)
8517 && !uData->present)
8518 info = 1;
8519 uData->seen = now;
8520 uData->present = 1;
8521 }
8522 }
8523
8524 /* If user joining normally (not during burst), apply op or voice,
8525 * and send greeting/userinfo as appropriate.
8526 */
8527 if(!user->uplink->burst)
8528 {
8529 if(modes)
8530 {
8531 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
8532 if(modes & MODE_CHANOP) {
8533 modes &= ~MODE_HALFOP;
8534 modes &= ~MODE_VOICE;
8535 }
8536 */
8537 change.args[0].mode = modes;
8538 change.args[0].u.member = mNode;
8539 mod_chanmode_announce(chanserv, channel, &change);
8540 }
8541 if(greeting)
8542 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
8543 if(uData && info)
8544 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
8545 }
8546 return 0;
8547 }
8548
8549 static void
8550 chanserv_autojoin_channels(struct userNode *user)
8551 {
8552 struct userData *channel;
8553
8554 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
8555 {
8556 struct chanNode *cn;
8557 struct modeNode *mn;
8558
8559 if(IsUserSuspended(channel)
8560 || IsSuspended(channel->channel)
8561 || !(cn = channel->channel->channel))
8562 continue;
8563
8564 mn = GetUserMode(cn, user);
8565 if(!mn)
8566 {
8567 if(!IsUserSuspended(channel)
8568 && IsUserAutoJoin(channel)
8569 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
8570 && !self->burst
8571 && !user->uplink->burst)
8572 irc_svsjoin(chanserv, user, cn);
8573 }
8574 }
8575 }
8576
8577 static void
8578 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra))
8579 {
8580 struct mod_chanmode change;
8581 struct userData *channel;
8582 unsigned int ii, jj, i;
8583
8584 if(!user->handle_info)
8585 return;
8586
8587 mod_chanmode_init(&change);
8588 change.argc = 1;
8589 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
8590 {
8591 struct chanNode *cn;
8592 struct chanData *cData;
8593 struct modeNode *mn;
8594 if(IsUserSuspended(channel)
8595 || IsSuspended(channel->channel)
8596 || !(cn = channel->channel->channel))
8597 continue;
8598
8599 cData = cn->channel_info;
8600 mn = GetUserMode(cn, user);
8601 if(!mn)
8602 {
8603 if(!IsUserSuspended(channel)
8604 && IsUserAutoInvite(channel)
8605 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
8606 && !self->burst
8607 && !user->uplink->burst)
8608 irc_invite(chanserv, user, cn);
8609 continue;
8610 }
8611
8612 if(channel->access >= UL_PRESENT)
8613 channel->channel->visited = now;
8614
8615 if(IsUserAutoOp(channel) && cData->chOpts[chAutomode] != 'n')
8616 {
8617 if(channel->access >= UL_OP )
8618 change.args[0].mode = MODE_CHANOP;
8619 else if(channel->access >= UL_HALFOP )
8620 change.args[0].mode = MODE_HALFOP;
8621 else if(channel->access >= UL_PEON )
8622 change.args[0].mode = MODE_VOICE;
8623 else
8624 change.args[0].mode = 0;
8625 change.args[0].u.member = mn;
8626 if(change.args[0].mode)
8627 mod_chanmode_announce(chanserv, cn, &change);
8628 }
8629
8630 channel->seen = now;
8631 channel->present = 1;
8632 }
8633
8634 for(ii = 0; ii < user->channels.used; ++ii)
8635 {
8636 struct chanNode *chan = user->channels.list[ii]->channel;
8637 struct banData *ban;
8638
8639 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
8640 || !chan->channel_info
8641 || IsSuspended(chan->channel_info))
8642 continue;
8643 if(protect_user(user, chanserv, chan->channel_info, true))
8644 continue;
8645 for(jj = 0; jj < chan->banlist.used; ++jj)
8646 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK, 0))
8647 break;
8648 if(jj < chan->banlist.used)
8649 continue;
8650 for(ban = chan->channel_info->bans; ban; ban = ban->next)
8651 {
8652 char kick_reason[MAXLEN];
8653 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE, 0))
8654 continue;
8655 change.args[0].mode = MODE_BAN;
8656 change.args[0].u.hostmask = ban->mask;
8657 mod_chanmode_announce(chanserv, chan, &change);
8658 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
8659 KickChannelUser(user, chan, chanserv, kick_reason);
8660 ban->triggered = now;
8661 break;
8662 }
8663 }
8664
8665 if(IsSupportHelper(user))
8666 {
8667 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8668 {
8669 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
8670 {
8671 HANDLE_SET_FLAG(user->handle_info, HELPING);
8672 break;
8673 }
8674 }
8675 }
8676
8677 if (user->handle_info->ignores->used) {
8678 for (i=0; i < user->handle_info->ignores->used; i++) {
8679 irc_silence(user, user->handle_info->ignores->list[i], 1);
8680 }
8681 }
8682
8683 if (user->handle_info->epithet)
8684 irc_swhois(chanserv, user, user->handle_info->epithet);
8685
8686 /* process autojoin channels 5 seconds later as this sometimes
8687 happens before autohide */
8688 // timeq_add(now + 5, chanserv_autojoin_channels, user);
8689 chanserv_autojoin_channels(user);
8690 }
8691
8692 static void
8693 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason), UNUSED_ARG(void *extra))
8694 {
8695 struct chanData *cData;
8696 struct userData *uData;
8697
8698 cData = mn->channel->channel_info;
8699 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
8700 return;
8701
8702 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
8703 {
8704 /* Allow for a bit of padding so that the limit doesn't
8705 track the user count exactly, which could get annoying. */
8706 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
8707 {
8708 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8709 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8710 }
8711 }
8712
8713 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
8714 {
8715 scan_user_presence(uData, mn->user);
8716 uData->seen = now;
8717 if (uData->access >= UL_PRESENT)
8718 cData->visited = now;
8719 }
8720
8721 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
8722 {
8723 unsigned int ii;
8724 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
8725 struct chanNode *channel;
8726 struct userNode *exclude;
8727 /* When looking at the channel that is being /part'ed, we
8728 * have to skip over the client that is leaving. For
8729 * other channels, we must not do that.
8730 */
8731 channel = chanserv_conf.support_channels.list[ii];
8732 exclude = (channel == mn->channel) ? mn->user : NULL;
8733 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
8734 break;
8735 }
8736 if(ii == chanserv_conf.support_channels.used)
8737 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
8738 }
8739 }
8740
8741 static void
8742 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel, UNUSED_ARG(void *extra))
8743 {
8744 struct userData *uData;
8745
8746 if(!channel->channel_info || !kicker || IsService(kicker)
8747 || (kicker == victim) || IsSuspended(channel->channel_info)
8748 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
8749 return;
8750
8751 if(protect_user(victim, kicker, channel->channel_info, false))
8752 {
8753 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
8754 KickChannelUser(kicker, channel, chanserv, reason);
8755 }
8756
8757 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
8758 uData->seen = now;
8759 }
8760
8761 static int
8762 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic, UNUSED_ARG(void *extra))
8763 {
8764 struct chanData *cData;
8765
8766 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
8767 return 0;
8768
8769 cData = channel->channel_info;
8770 if(bad_topic(channel, user, channel->topic))
8771 { /* User doesnt have privs to set topics. Undo it */
8772 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
8773 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
8774 return 1;
8775 }
8776 /* If there is a topic mask set, and the new topic doesnt match,
8777 * set the topic to mask + new_topic */
8778 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
8779 {
8780 char new_topic[TOPICLEN+1];
8781 conform_topic(cData->topic_mask, channel->topic, new_topic);
8782 if(*new_topic)
8783 {
8784 SetChannelTopic(channel, chanserv, user, new_topic, 1);
8785 /* and fall through to topicsnarf code below.. */
8786 }
8787 else /* Topic couldnt fit into mask, was too long */
8788 {
8789 SetChannelTopic(channel, chanserv, user, old_topic, 1);
8790 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
8791 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
8792 return 1;
8793 }
8794 }
8795 /* With topicsnarf, grab the topic and save it as the default topic. */
8796 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
8797 {
8798 free(cData->topic);
8799 cData->topic = strdup(channel->topic);
8800 }
8801 return 0;
8802 }
8803
8804 static void
8805 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change, UNUSED_ARG(void *extra))
8806 {
8807 struct mod_chanmode *bounce = NULL;
8808 unsigned int bnc, ii;
8809 char deopped = 0;
8810
8811 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
8812 return;
8813
8814 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
8815 && mode_lock_violated(&channel->channel_info->modes, change))
8816 {
8817 char correct[MAXLEN];
8818 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
8819 mod_chanmode_format(&channel->channel_info->modes, correct);
8820 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
8821 }
8822 for(ii = bnc = 0; ii < change->argc; ++ii)
8823 {
8824 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
8825 {
8826 const struct userNode *victim = change->args[ii].u.member->user;
8827 if(!protect_user(victim, user, channel->channel_info, false))
8828 continue;
8829 if(!bounce)
8830 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8831 if(!deopped)
8832 {
8833 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
8834 bounce->args[bnc].u.member = GetUserMode(channel, user);
8835 if(bounce->args[bnc].u.member)
8836 bnc++;
8837 deopped = 1;
8838 }
8839 bounce->args[bnc].mode = MODE_CHANOP;
8840 bounce->args[bnc].u.member = change->args[ii].u.member;
8841 bnc++;
8842 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
8843 }
8844 else if(change->args[ii].mode & MODE_CHANOP)
8845 {
8846 const struct userNode *victim = change->args[ii].u.member->user;
8847 if(IsService(victim) || validate_op(NULL, user, channel, (struct userNode*)victim))
8848 continue;
8849 if(!bounce)
8850 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8851 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
8852 bounce->args[bnc].u.member = change->args[ii].u.member;
8853 bnc++;
8854 }
8855 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
8856 {
8857 const char *ban = change->args[ii].u.hostmask;
8858 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
8859 continue;
8860 if(!bounce)
8861 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8862 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
8863 bounce->args[bnc].u.hostmask = strdup(ban);
8864 bnc++;
8865 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
8866 }
8867 }
8868 if(bounce)
8869 {
8870 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
8871 mod_chanmode_announce(chanserv, channel, bounce);
8872 for(ii = 0; ii < change->argc; ++ii)
8873 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
8874 free((char*)bounce->args[ii].u.hostmask);
8875 mod_chanmode_free(bounce);
8876 }
8877 }
8878
8879 static void
8880 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick), UNUSED_ARG(void *extra))
8881 {
8882 struct chanNode *channel;
8883 struct banData *bData;
8884 struct mod_chanmode change;
8885 unsigned int ii, jj;
8886 char kick_reason[MAXLEN];
8887
8888 mod_chanmode_init(&change);
8889 change.argc = 1;
8890 change.args[0].mode = MODE_BAN;
8891 for(ii = 0; ii < user->channels.used; ++ii)
8892 {
8893 channel = user->channels.list[ii]->channel;
8894 /* Need not check for bans if they're opped or voiced. */
8895 /* TODO: does this make sense in automode v, h, and o? *
8896 * lets still enforce on voice people anyway, and see how that goes -Rubin */
8897 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE ))
8898 continue;
8899 /* Need not check for bans unless channel registration is active. */
8900 if(!channel->channel_info || IsSuspended(channel->channel_info))
8901 continue;
8902 /* Look for a matching ban already on the channel. */
8903 for(jj = 0; jj < channel->banlist.used; ++jj)
8904 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK, 0))
8905 break;
8906 /* Need not act if we found one. */
8907 if(jj < channel->banlist.used)
8908 continue;
8909 /* don't kick someone on the userlist */
8910 if(protect_user(user, chanserv, channel->channel_info, true))
8911 continue;
8912 /* Look for a matching ban in this channel. */
8913 for(bData = channel->channel_info->bans; bData; bData = bData->next)
8914 {
8915 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE, 0))
8916 continue;
8917 change.args[0].u.hostmask = bData->mask;
8918 mod_chanmode_announce(chanserv, channel, &change);
8919 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8920 KickChannelUser(user, channel, chanserv, kick_reason);
8921 bData->triggered = now;
8922 break; /* we don't need to check any more bans in the channel */
8923 }
8924 }
8925 }
8926
8927 static void handle_rename(struct handle_info *handle, const char *old_handle, UNUSED_ARG(void *extra))
8928 {
8929 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
8930
8931 if(dnr)
8932 {
8933 dict_remove2(handle_dnrs, old_handle, 1);
8934 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
8935 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
8936 }
8937 }
8938
8939 static void
8940 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle, UNUSED_ARG(void *extra))
8941 {
8942 struct userNode *h_user;
8943
8944 if(handle->channels)
8945 {
8946 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
8947 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
8948
8949 while(handle->channels)
8950 del_channel_user(handle->channels, 1);
8951 }
8952 }
8953
8954 static int
8955 handle_server_link(UNUSED_ARG(struct server *server), UNUSED_ARG(void *extra))
8956 {
8957 struct chanData *cData;
8958
8959 for(cData = channelList; cData; cData = cData->next)
8960 {
8961 if(!IsSuspended(cData))
8962 cData->may_opchan = 1;
8963 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
8964 && !cData->channel->join_flooded
8965 && ((cData->channel->limit - cData->channel->members.used)
8966 < chanserv_conf.adjust_threshold))
8967 {
8968 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8969 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8970 }
8971 }
8972 return 0;
8973 }
8974
8975 static void
8976 chanserv_conf_read(void)
8977 {
8978 dict_t conf_node;
8979 const char *str;
8980 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
8981 struct mod_chanmode *change;
8982 struct string_list *strlist;
8983 struct chanNode *chan;
8984 unsigned int ii;
8985
8986 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
8987 {
8988 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
8989 return;
8990 }
8991 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8992 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8993 chanserv_conf.support_channels.used = 0;
8994 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
8995 {
8996 for(ii = 0; ii < strlist->used; ++ii)
8997 {
8998 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
8999 if(!str2)
9000 str2 = "+nt";
9001 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
9002 LockChannel(chan);
9003 channelList_append(&chanserv_conf.support_channels, chan);
9004 }
9005 }
9006 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
9007 {
9008 const char *str2;
9009 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
9010 if(!str2)
9011 str2 = "+nt";
9012 chan = AddChannel(str, now, str2, NULL, NULL);
9013 LockChannel(chan);
9014 channelList_append(&chanserv_conf.support_channels, chan);
9015 }
9016 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
9017 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
9018 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
9019 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
9020 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
9021 chanserv_conf.greeting_length = str ? atoi(str) : 200;
9022 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
9023 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
9024 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
9025 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
9026 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
9027 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
9028 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
9029 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
9030 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
9031 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
9032 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
9033 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
9034 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
9035 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
9036 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
9037 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
9038 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
9039 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
9040 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
9041 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
9042 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
9043 if(chanserv && str)
9044 NickChange(chanserv, str, 0);
9045 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
9046 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
9047 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
9048 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
9049 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
9050 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
9051 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
9052 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
9053 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
9054 chanserv_conf.max_owned = str ? atoi(str) : 5;
9055 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
9056 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
9057 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
9058 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
9059 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
9060 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
9061 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
9062 god_timeout = str ? ParseInterval(str) : 60*15;
9063 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
9064 if(!str)
9065 str = "+nt";
9066 safestrncpy(mode_line, str, sizeof(mode_line));
9067 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
9068 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
9069 && (change->argc < 2))
9070 {
9071 chanserv_conf.default_modes = *change;
9072 mod_chanmode_free(change);
9073 }
9074 str = database_get_data(conf_node, KEY_VALID_CHANNEL_REGEX, RECDB_QSTRING);
9075 if (chanserv_conf.valid_channel_regex_set)
9076 regfree(&chanserv_conf.valid_channel_regex);
9077 if (str) {
9078 int err = regcomp(&chanserv_conf.valid_channel_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
9079 chanserv_conf.valid_channel_regex_set = !err;
9080 if (err) log_module(CS_LOG, LOG_ERROR, "Bad valid_channel_regex (error %d)", err);
9081 } else {
9082 chanserv_conf.valid_channel_regex_set = 0;
9083 }
9084 free_string_list(chanserv_conf.wheel);
9085 strlist = database_get_data(conf_node, "wheel", RECDB_STRING_LIST);
9086 if(strlist)
9087 strlist = string_list_copy(strlist);
9088 else
9089 {
9090 static const char *list[] = {
9091 "peer", "partall", "gline", /* "shun", */
9092 "nothing", "randjoin", "abusewhois", "kickall",
9093 "nickchange", "kill", "svsignore", "kickbanall",
9094 NULL};
9095 unsigned int ii;
9096 strlist = alloc_string_list(ArrayLength(list)-1);
9097 for(ii=0; list[ii]; ii++)
9098 string_list_append(strlist, strdup(list[ii]));
9099 }
9100 chanserv_conf.wheel = strlist;
9101
9102 free_string_list(chanserv_conf.set_shows);
9103 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
9104 if(strlist)
9105 strlist = string_list_copy(strlist);
9106 else
9107 {
9108 static const char *list[] = {
9109 /* free form text */
9110 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9111 /* options based on user level */
9112 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9113 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
9114 /* multiple choice options */
9115 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
9116 /* binary options */
9117 "DynLimit", "NoDelete", "BanTimeout",
9118 /* delimiter */
9119 NULL
9120 };
9121 strlist = alloc_string_list(ArrayLength(list)-1);
9122 for(ii=0; list[ii]; ii++)
9123 string_list_append(strlist, strdup(list[ii]));
9124 }
9125 chanserv_conf.set_shows = strlist;
9126 /* We don't look things up now, in case the list refers to options
9127 * defined by modules initialized after this point. Just mark the
9128 * function list as invalid, so it will be initialized.
9129 */
9130 set_shows_list.used = 0;
9131
9132 free_string_list(chanserv_conf.eightball);
9133 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
9134 if(strlist)
9135 {
9136 strlist = string_list_copy(strlist);
9137 }
9138 else
9139 {
9140 strlist = alloc_string_list(4);
9141 string_list_append(strlist, strdup("Yes."));
9142 string_list_append(strlist, strdup("No."));
9143 string_list_append(strlist, strdup("Maybe so."));
9144 }
9145 chanserv_conf.eightball = strlist;
9146
9147 free_string_list(chanserv_conf.old_ban_names);
9148 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
9149 if(strlist)
9150 strlist = string_list_copy(strlist);
9151 else
9152 strlist = alloc_string_list(2);
9153 chanserv_conf.old_ban_names = strlist;
9154 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
9155 off_channel = str ? atoi(str) : 0;
9156 }
9157
9158 static void
9159 chanserv_note_type_read(const char *key, struct record_data *rd)
9160 {
9161 dict_t obj;
9162 struct note_type *ntype;
9163 const char *str;
9164
9165 if(!(obj = GET_RECORD_OBJECT(rd)))
9166 {
9167 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
9168 return;
9169 }
9170 if(!(ntype = chanserv_create_note_type(key)))
9171 {
9172 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
9173 return;
9174 }
9175
9176 /* Figure out set access */
9177 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
9178 {
9179 ntype->set_access_type = NOTE_SET_PRIVILEGED;
9180 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
9181 }
9182 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
9183 {
9184 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
9185 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
9186 }
9187 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
9188 {
9189 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
9190 }
9191 else
9192 {
9193 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
9194 ntype->set_access_type = NOTE_SET_PRIVILEGED;
9195 ntype->set_access.min_opserv = 0;
9196 }
9197
9198 /* Figure out visibility */
9199 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
9200 ntype->visible_type = NOTE_VIS_PRIVILEGED;
9201 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
9202 ntype->visible_type = NOTE_VIS_PRIVILEGED;
9203 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
9204 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
9205 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
9206 ntype->visible_type = NOTE_VIS_ALL;
9207 else
9208 ntype->visible_type = NOTE_VIS_PRIVILEGED;
9209
9210 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
9211 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
9212 }
9213
9214 static void
9215 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
9216 {
9217 struct handle_info *handle;
9218 struct userData *uData;
9219 char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc;
9220 time_t last_seen;
9221 unsigned short access_level, lastaccess = 0;
9222
9223 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
9224 {
9225 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
9226 return;
9227 }
9228
9229 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
9230 if(access_level > UL_OWNER)
9231 {
9232 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
9233 return;
9234 }
9235
9236 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
9237 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
9238 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
9239 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
9240 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
9241 accessexpiry = database_get_data(rd->d.object, KEY_ACCESSEXPIRY, RECDB_QSTRING);
9242 clvlexpiry = database_get_data(rd->d.object, KEY_CLVLEXPIRY, RECDB_QSTRING);
9243 lstacc = database_get_data(rd->d.object, KEY_LASTLEVEL, RECDB_QSTRING);
9244 lastaccess = lstacc ? atoi(lstacc) : 0;
9245
9246 handle = get_handle_info(key);
9247 if(!handle)
9248 {
9249 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
9250 return;
9251 }
9252
9253 uData = add_channel_user(chan, handle, access_level, last_seen, inf, 0);
9254 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
9255 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
9256
9257 uData->accessexpiry = accessexpiry ? strtoul(accessexpiry, NULL, 0) : 0;
9258 if (uData->accessexpiry > 0)
9259 timeq_add(uData->accessexpiry, chanserv_expire_tempuser, uData);
9260
9261 uData->clvlexpiry = clvlexpiry ? strtoul(clvlexpiry, NULL, 0) : 0;
9262 if (uData->clvlexpiry > 0)
9263 timeq_add(uData->clvlexpiry, chanserv_expire_tempclvl, uData);
9264
9265 uData->lastaccess = lastaccess;
9266
9267 if((uData->flags & USER_SUSPENDED) && uData->expires)
9268 {
9269 if(uData->expires > now)
9270 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
9271 else
9272 uData->flags &= ~USER_SUSPENDED;
9273 }
9274
9275 /* Upgrade: set autoop to the inverse of noautoop */
9276 if(chanserv_read_version < 2)
9277 {
9278 /* if noautoop is true, set autoop false, and vice versa */
9279 if(uData->flags & USER_NOAUTO_OP)
9280 uData->flags = uData->flags & ~USER_AUTO_OP;
9281 else
9282 uData->flags = uData->flags | USER_AUTO_OP;
9283 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);
9284 }
9285
9286 }
9287
9288 static void
9289 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
9290 {
9291 struct banData *bData;
9292 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
9293 time_t set_time, triggered_time, expires_time;
9294
9295 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
9296 {
9297 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
9298 return;
9299 }
9300
9301 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
9302 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
9303 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
9304 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
9305 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
9306 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
9307 if (!reason || !owner)
9308 return;
9309
9310 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
9311 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
9312 if(s_expires)
9313 expires_time = (time_t)strtoul(s_expires, NULL, 0);
9314 else if(s_duration)
9315 expires_time = set_time + atoi(s_duration);
9316 else
9317 expires_time = 0;
9318
9319 if(!reason || (expires_time && (expires_time < now)))
9320 return;
9321
9322 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
9323 }
9324
9325 static struct suspended *
9326 chanserv_read_suspended(dict_t obj)
9327 {
9328 struct suspended *suspended = calloc(1, sizeof(*suspended));
9329 char *str;
9330 dict_t previous;
9331
9332 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
9333 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
9334 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
9335 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
9336 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
9337 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
9338 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
9339 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
9340 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
9341 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
9342 return suspended;
9343 }
9344
9345 static struct giveownership *
9346 chanserv_read_giveownership(dict_t obj)
9347 {
9348 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
9349 char *str;
9350 dict_t previous;
9351
9352 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
9353 giveownership->staff_issuer = str ? strdup(str) : NULL;
9354
9355 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
9356
9357 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
9358 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
9359
9360 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
9361 giveownership->reason = str ? strdup(str) : NULL;
9362 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
9363 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
9364
9365 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
9366 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
9367 return giveownership;
9368 }
9369
9370 static int
9371 chanserv_channel_read(const char *key, struct record_data *hir)
9372 {
9373 struct suspended *suspended;
9374 struct giveownership *giveownership;
9375 struct mod_chanmode *modes;
9376 struct chanNode *cNode;
9377 struct chanData *cData;
9378 struct dict *channel, *obj;
9379 char *str, *argv[10];
9380 dict_iterator_t it;
9381 unsigned int argc;
9382
9383 channel = hir->d.object;
9384
9385 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
9386 if(!str)
9387 str = "<unknown>";
9388 cNode = AddChannel(key, now, NULL, NULL, NULL);
9389 if(!cNode)
9390 {
9391 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
9392 return 0;
9393 }
9394 cData = register_channel(cNode, str);
9395 if(!cData)
9396 {
9397 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
9398 return 0;
9399 }
9400
9401 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
9402 {
9403 enum levelOption lvlOpt;
9404 enum charOption chOpt;
9405
9406 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
9407 cData->flags = atoi(str);
9408
9409 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9410 {
9411 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
9412 if(str)
9413 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
9414 else if(levelOptions[lvlOpt].old_flag)
9415 {
9416 if(cData->flags & levelOptions[lvlOpt].old_flag)
9417 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
9418 else
9419 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
9420 }
9421 }
9422
9423 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9424 {
9425 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
9426 continue;
9427 cData->chOpts[chOpt] = str[0];
9428 }
9429 }
9430 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
9431 {
9432 enum levelOption lvlOpt;
9433 enum charOption chOpt;
9434 unsigned int count;
9435
9436 cData->flags = base64toint(str, 5);
9437 count = strlen(str += 5);
9438 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9439 {
9440 unsigned short lvl;
9441 if(levelOptions[lvlOpt].old_flag)
9442 {
9443 if(cData->flags & levelOptions[lvlOpt].old_flag)
9444 lvl = levelOptions[lvlOpt].flag_value;
9445 else
9446 lvl = levelOptions[lvlOpt].default_value;
9447 }
9448 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
9449 {
9450 case 'c': lvl = UL_COOWNER; break;
9451 case 'm': lvl = UL_MANAGER; break;
9452 case 'n': lvl = UL_OWNER+1; break;
9453 case 'o': lvl = UL_OP; break;
9454 case 'p': lvl = UL_PEON; break;
9455 case 'h': lvl = UL_HALFOP; break;
9456 case 'w': lvl = UL_OWNER; break;
9457 default: lvl = 0; break;
9458 }
9459 cData->lvlOpts[lvlOpt] = lvl;
9460 }
9461 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9462 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
9463 }
9464
9465 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
9466 {
9467 suspended = chanserv_read_suspended(obj);
9468 cData->suspended = suspended;
9469 suspended->cData = cData;
9470 /* We could use suspended->expires and suspended->revoked to
9471 * set the CHANNEL_SUSPENDED flag, but we don't. */
9472 }
9473 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
9474 {
9475 suspended = calloc(1, sizeof(*suspended));
9476 suspended->issued = 0;
9477 suspended->revoked = 0;
9478 suspended->suspender = strdup(str);
9479 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
9480 suspended->expires = str ? atoi(str) : 0;
9481 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
9482 suspended->reason = strdup(str ? str : "No reason");
9483 suspended->previous = NULL;
9484 cData->suspended = suspended;
9485 suspended->cData = cData;
9486 }
9487 else
9488 {
9489 cData->flags &= ~CHANNEL_SUSPENDED;
9490 suspended = NULL; /* to squelch a warning */
9491 }
9492
9493 if(IsSuspended(cData)) {
9494 if(suspended->expires > now)
9495 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
9496 else if(suspended->expires)
9497 cData->flags &= ~CHANNEL_SUSPENDED;
9498 }
9499
9500 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
9501 {
9502 giveownership = chanserv_read_giveownership(obj);
9503 cData->giveownership = giveownership;
9504 }
9505
9506 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
9507 struct mod_chanmode change;
9508 mod_chanmode_init(&change);
9509 change.argc = 1;
9510 change.args[0].mode = MODE_CHANOP;
9511 change.args[0].u.member = AddChannelUser(chanserv, cNode);
9512 mod_chanmode_announce(chanserv, cNode, &change);
9513 }
9514
9515 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
9516 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
9517 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
9518 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
9519 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
9520 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
9521 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
9522 cData->max = str ? atoi(str) : 0;
9523 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
9524 cData->greeting = str ? strdup(str) : NULL;
9525 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
9526 cData->user_greeting = str ? strdup(str) : NULL;
9527 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
9528 cData->topic_mask = str ? strdup(str) : NULL;
9529 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
9530 cData->topic = str ? strdup(str) : NULL;
9531
9532 str = database_get_data(channel, KEY_MAXSETINFO, RECDB_QSTRING);
9533 cData->maxsetinfo = str ? strtoul(str, NULL, 0) : chanserv_conf.max_userinfo_length;
9534
9535 if(!IsSuspended(cData)
9536 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
9537 && (argc = split_line(str, 0, ArrayLength(argv), argv))
9538 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
9539 cData->modes = *modes;
9540 if(off_channel > 0)
9541 cData->modes.modes_set |= MODE_REGISTERED;
9542 if(cData->modes.argc > 1)
9543 cData->modes.argc = 1;
9544 mod_chanmode_announce(chanserv, cNode, &cData->modes);
9545 mod_chanmode_free(modes);
9546 }
9547
9548 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
9549 for(it = dict_first(obj); it; it = iter_next(it))
9550 user_read_helper(iter_key(it), iter_data(it), cData);
9551
9552 if(!cData->users && !IsProtected(cData))
9553 {
9554 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
9555 unregister_channel(cData, "has empty user list.");
9556 return 0;
9557 }
9558
9559 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
9560 for(it = dict_first(obj); it; it = iter_next(it))
9561 ban_read_helper(iter_key(it), iter_data(it), cData);
9562
9563 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
9564 for(it = dict_first(obj); it; it = iter_next(it))
9565 {
9566 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
9567 struct record_data *rd = iter_data(it);
9568 const char *note, *setter;
9569
9570 if(rd->type != RECDB_OBJECT)
9571 {
9572 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
9573 }
9574 else if(!ntype)
9575 {
9576 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
9577 }
9578 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
9579 {
9580 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
9581 }
9582 else
9583 {
9584 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
9585 if(!setter) setter = "<unknown>";
9586 chanserv_add_channel_note(cData, ntype, setter, note);
9587 }
9588 }
9589
9590 return 0;
9591 }
9592
9593 static void
9594 chanserv_dnr_read(const char *key, struct record_data *hir)
9595 {
9596 const char *setter, *reason, *str;
9597 struct do_not_register *dnr;
9598 time_t expiry;
9599
9600 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
9601 if(!setter)
9602 {
9603 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
9604 return;
9605 }
9606 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
9607 if(!reason)
9608 {
9609 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
9610 return;
9611 }
9612 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
9613 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
9614 if(expiry && expiry <= now)
9615 return;
9616 dnr = chanserv_add_dnr(key, setter, expiry, reason);
9617 if(!dnr)
9618 return;
9619 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
9620 if(str)
9621 dnr->set = atoi(str);
9622 else
9623 dnr->set = 0;
9624 }
9625
9626 static void
9627 chanserv_version_read(struct dict *section)
9628 {
9629 /* global var.. */
9630 char *str;
9631 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
9632 if(str)
9633 chanserv_read_version = atoi(str);
9634 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
9635 }
9636
9637 static int
9638 chanserv_saxdb_read(struct dict *database)
9639 {
9640 struct dict *section;
9641 dict_iterator_t it;
9642
9643 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
9644 chanserv_version_read(section);
9645
9646 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
9647 for(it = dict_first(section); it; it = iter_next(it))
9648 chanserv_note_type_read(iter_key(it), iter_data(it));
9649
9650 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
9651 for(it = dict_first(section); it; it = iter_next(it))
9652 chanserv_channel_read(iter_key(it), iter_data(it));
9653
9654 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
9655 for(it = dict_first(section); it; it = iter_next(it))
9656 chanserv_dnr_read(iter_key(it), iter_data(it));
9657
9658 return 0;
9659 }
9660
9661 static int
9662 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
9663 {
9664 int high_present = 0;
9665 saxdb_start_record(ctx, KEY_USERS, 1);
9666 for(; uData; uData = uData->next)
9667 {
9668 if((uData->access >= UL_PRESENT) && uData->present)
9669 high_present = 1;
9670 saxdb_start_record(ctx, uData->handle->handle, 0);
9671 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
9672 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
9673 saxdb_write_int(ctx, KEY_ACCESSEXPIRY, uData->accessexpiry);
9674 saxdb_write_int(ctx, KEY_CLVLEXPIRY, uData->clvlexpiry);
9675 saxdb_write_int(ctx, KEY_LASTLEVEL, uData->lastaccess);
9676 if(uData->flags)
9677 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
9678 if(uData->expires)
9679 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
9680 if(uData->info)
9681 saxdb_write_string(ctx, KEY_INFO, uData->info);
9682 saxdb_end_record(ctx);
9683 }
9684 saxdb_end_record(ctx);
9685 return high_present;
9686 }
9687
9688 static void
9689 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
9690 {
9691 if(!bData)
9692 return;
9693 saxdb_start_record(ctx, KEY_BANS, 1);
9694 for(; bData; bData = bData->next)
9695 {
9696 saxdb_start_record(ctx, bData->mask, 0);
9697 saxdb_write_int(ctx, KEY_SET, bData->set);
9698 if(bData->triggered)
9699 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
9700 if(bData->expires)
9701 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
9702 if(bData->owner[0])
9703 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
9704 if(bData->reason)
9705 saxdb_write_string(ctx, KEY_REASON, bData->reason);
9706 saxdb_end_record(ctx);
9707 }
9708 saxdb_end_record(ctx);
9709 }
9710
9711 static void
9712 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
9713 {
9714 saxdb_start_record(ctx, name, 0);
9715 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
9716 saxdb_write_string(ctx, KEY_REASON, susp->reason);
9717 if(susp->issued)
9718 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
9719 if(susp->expires)
9720 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
9721 if(susp->revoked)
9722 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
9723 if(susp->previous)
9724 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
9725 saxdb_end_record(ctx);
9726 }
9727
9728 static void
9729 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
9730 {
9731 saxdb_start_record(ctx, name, 0);
9732 if(giveownership->staff_issuer)
9733 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
9734 if(giveownership->old_owner)
9735 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
9736 if(giveownership->target)
9737 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
9738 if(giveownership->target_access)
9739 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
9740 if(giveownership->reason)
9741 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
9742 if(giveownership->issued)
9743 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
9744 if(giveownership->previous)
9745 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
9746 saxdb_end_record(ctx);
9747 }
9748
9749 static void
9750 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
9751 {
9752 char buf[MAXLEN];
9753 int high_present;
9754 enum levelOption lvlOpt;
9755 enum charOption chOpt;
9756
9757 saxdb_start_record(ctx, channel->channel->name, 1);
9758
9759 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
9760 saxdb_write_int(ctx, KEY_MAX, channel->max);
9761 if(channel->topic)
9762 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
9763 if(channel->registrar)
9764 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
9765 if(channel->greeting)
9766 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
9767 if(channel->user_greeting)
9768 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
9769 if(channel->topic_mask)
9770 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
9771 if(channel->suspended)
9772 chanserv_write_suspended(ctx, "suspended", channel->suspended);
9773 if(channel->giveownership)
9774 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
9775
9776 saxdb_start_record(ctx, KEY_OPTIONS, 0);
9777 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
9778 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9779 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
9780 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9781 {
9782 buf[0] = channel->chOpts[chOpt];
9783 buf[1] = '\0';
9784 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
9785 }
9786 saxdb_end_record(ctx);
9787
9788 if (channel->maxsetinfo)
9789 saxdb_write_int(ctx, KEY_MAXSETINFO, channel->maxsetinfo);
9790
9791 if(channel->modes.modes_set || channel->modes.modes_clear)
9792 {
9793 mod_chanmode_format(&channel->modes, buf);
9794 saxdb_write_string(ctx, KEY_MODES, buf);
9795 }
9796
9797 high_present = chanserv_write_users(ctx, channel->users);
9798 chanserv_write_bans(ctx, channel->bans);
9799
9800 if(dict_size(channel->notes))
9801 {
9802 dict_iterator_t it;
9803
9804 saxdb_start_record(ctx, KEY_NOTES, 1);
9805 for(it = dict_first(channel->notes); it; it = iter_next(it))
9806 {
9807 struct note *note = iter_data(it);
9808 saxdb_start_record(ctx, iter_key(it), 0);
9809 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
9810 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
9811 saxdb_end_record(ctx);
9812 }
9813 saxdb_end_record(ctx);
9814 }
9815
9816 if(channel->ownerTransfer)
9817 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
9818 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
9819 saxdb_end_record(ctx);
9820 }
9821
9822 static void
9823 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
9824 {
9825 const char *str;
9826
9827 saxdb_start_record(ctx, ntype->name, 0);
9828 switch(ntype->set_access_type)
9829 {
9830 case NOTE_SET_CHANNEL_ACCESS:
9831 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
9832 break;
9833 case NOTE_SET_CHANNEL_SETTER:
9834 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
9835 break;
9836 case NOTE_SET_PRIVILEGED: default:
9837 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
9838 break;
9839 }
9840 switch(ntype->visible_type)
9841 {
9842 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
9843 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
9844 case NOTE_VIS_PRIVILEGED:
9845 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
9846 }
9847 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
9848 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
9849 saxdb_end_record(ctx);
9850 }
9851
9852 static void
9853 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
9854 {
9855 struct do_not_register *dnr;
9856 dict_iterator_t it, next;
9857
9858 for(it = dict_first(dnrs); it; it = next)
9859 {
9860 next = iter_next(it);
9861 dnr = iter_data(it);
9862 if(dnr->expires && dnr->expires <= now)
9863 continue;
9864 saxdb_start_record(ctx, dnr->chan_name, 0);
9865 if(dnr->set)
9866 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
9867 if(dnr->expires)
9868 {
9869 dict_remove(dnrs, iter_key(it));
9870 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
9871 }
9872 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
9873 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
9874 saxdb_end_record(ctx);
9875 }
9876 }
9877
9878 static int
9879 chanserv_saxdb_write(struct saxdb_context *ctx)
9880 {
9881 dict_iterator_t it;
9882 struct chanData *channel;
9883
9884 /* Version Control*/
9885 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
9886 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
9887 saxdb_end_record(ctx);
9888
9889 /* Notes */
9890 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
9891 for(it = dict_first(note_types); it; it = iter_next(it))
9892 chanserv_write_note_type(ctx, iter_data(it));
9893 saxdb_end_record(ctx);
9894
9895 /* DNRs */
9896 saxdb_start_record(ctx, KEY_DNR, 1);
9897 write_dnrs_helper(ctx, handle_dnrs);
9898 write_dnrs_helper(ctx, plain_dnrs);
9899 write_dnrs_helper(ctx, mask_dnrs);
9900 saxdb_end_record(ctx);
9901
9902 /* Channels */
9903 saxdb_start_record(ctx, KEY_CHANNELS, 1);
9904 for(channel = channelList; channel; channel = channel->next)
9905 chanserv_write_channel(ctx, channel);
9906 saxdb_end_record(ctx);
9907
9908 return 0;
9909 }
9910
9911 static void
9912 chanserv_db_cleanup(UNUSED_ARG(void *extra)) {
9913 unsigned int ii;
9914 unreg_part_func(handle_part, NULL);
9915 while(channelList)
9916 unregister_channel(channelList, "terminating.");
9917 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
9918 UnlockChannel(chanserv_conf.support_channels.list[ii]);
9919 free(chanserv_conf.support_channels.list);
9920 dict_delete(handle_dnrs);
9921 dict_delete(plain_dnrs);
9922 dict_delete(mask_dnrs);
9923 dict_delete(note_types);
9924 free_string_list(chanserv_conf.eightball);
9925 free_string_list(chanserv_conf.old_ban_names);
9926 free_string_list(chanserv_conf.wheel);
9927 free_string_list(chanserv_conf.set_shows);
9928 free(set_shows_list.list);
9929 free(uset_shows_list.list);
9930 while(helperList)
9931 {
9932 struct userData *helper = helperList;
9933 helperList = helperList->next;
9934 free(helper);
9935 }
9936 }
9937
9938 #if defined(GCC_VARMACROS)
9939 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
9940 #elif defined(C99_VARMACROS)
9941 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
9942 #endif
9943 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
9944 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
9945
9946 void
9947 init_chanserv(const char *nick)
9948 {
9949 struct chanNode *chan;
9950 unsigned int i;
9951
9952 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
9953 conf_register_reload(chanserv_conf_read);
9954
9955 if (nick) {
9956 reg_server_link_func(handle_server_link, NULL);
9957 reg_new_channel_func(handle_new_channel, NULL);
9958 reg_join_func(handle_join, NULL);
9959 reg_part_func(handle_part, NULL);
9960 reg_kick_func(handle_kick, NULL);
9961 reg_topic_func(handle_topic, NULL);
9962 reg_mode_change_func(handle_mode, NULL);
9963 reg_nick_change_func(handle_nick_change, NULL);
9964 reg_auth_func(handle_auth, NULL);
9965 }
9966
9967 reg_handle_rename_func(handle_rename, NULL);
9968 reg_unreg_func(handle_unreg, NULL);
9969
9970 handle_dnrs = dict_new();
9971 dict_set_free_data(handle_dnrs, free);
9972 plain_dnrs = dict_new();
9973 dict_set_free_data(plain_dnrs, free);
9974 mask_dnrs = dict_new();
9975 dict_set_free_data(mask_dnrs, free);
9976
9977 reg_svccmd_unbind_func(handle_svccmd_unbind, NULL);
9978 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
9979 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+channel", NULL);
9980 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
9981 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
9982 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
9983 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
9984 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
9985 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
9986 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
9987 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN|MODCMD_IGNORE_CSUSPEND, "flags", "+helping", NULL);
9988 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN|MODCMD_IGNORE_CSUSPEND, "flags", "+helping", NULL);
9989 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
9990 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
9991
9992 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
9993
9994 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
9995 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
9996
9997 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9998 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
9999 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10000 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10001 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
10002
10003 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
10004 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
10005 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
10006 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10007 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10008 DEFINE_COMMAND(mdelpal, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10009 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10010
10011 DEFINE_COMMAND(levels, 1, 0, NULL);
10012
10013 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10014 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
10015 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10016 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
10017
10018 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
10019 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
10020 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
10021 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
10022 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
10023 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
10024 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
10025 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
10026 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
10027 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
10028
10029 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
10030 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
10031 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
10032 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
10033 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
10034 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
10035 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
10036 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
10037 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
10038 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
10039 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
10040 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
10041 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10042 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10043
10044 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
10045 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
10046 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
10047 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
10048
10049 /* if you change dellamer access, see also places
10050 * like unbanme which have manager hardcoded. */
10051 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
10052 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
10053
10054 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
10055
10056 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
10057
10058 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
10059 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10060 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10061 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10062 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10063 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10064 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10065 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10066 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10067 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10068 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10069 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10070
10071 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
10072 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
10073
10074 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
10075 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
10076 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
10077 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
10078
10079 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
10080 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
10081 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
10082 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
10083 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
10084
10085 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10086 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10087 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10088 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10089 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10090 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10091 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10092 DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10093 DEFINE_COMMAND(roulette, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10094 DEFINE_COMMAND(shoot, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10095 DEFINE_COMMAND(spin, 1, MODCMD_REQUIRE_AUTHED, "spin", "+nolog,+toy,+acceptchan", NULL);
10096
10097 /* Channel options */
10098 DEFINE_CHANNEL_OPTION(defaulttopic);
10099 DEFINE_CHANNEL_OPTION(topicmask);
10100 DEFINE_CHANNEL_OPTION(greeting);
10101 DEFINE_CHANNEL_OPTION(usergreeting);
10102 DEFINE_CHANNEL_OPTION(modes);
10103 DEFINE_CHANNEL_OPTION(enfops);
10104 DEFINE_CHANNEL_OPTION(enfhalfops);
10105 DEFINE_CHANNEL_OPTION(automode);
10106 DEFINE_CHANNEL_OPTION(protect);
10107 DEFINE_CHANNEL_OPTION(enfmodes);
10108 DEFINE_CHANNEL_OPTION(enftopic);
10109 DEFINE_CHANNEL_OPTION(pubcmd);
10110 DEFINE_CHANNEL_OPTION(userinfo);
10111 DEFINE_CHANNEL_OPTION(dynlimit);
10112 DEFINE_CHANNEL_OPTION(topicsnarf);
10113 DEFINE_CHANNEL_OPTION(nodelete);
10114 DEFINE_CHANNEL_OPTION(toys);
10115 DEFINE_CHANNEL_OPTION(setters);
10116 DEFINE_CHANNEL_OPTION(topicrefresh);
10117 DEFINE_CHANNEL_OPTION(resync);
10118 DEFINE_CHANNEL_OPTION(ctcpreaction);
10119 DEFINE_CHANNEL_OPTION(bantimeout);
10120 DEFINE_CHANNEL_OPTION(inviteme);
10121 DEFINE_CHANNEL_OPTION(unreviewed);
10122 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
10123 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
10124 DEFINE_CHANNEL_OPTION(maxsetinfo);
10125 if(off_channel > 1)
10126 DEFINE_CHANNEL_OPTION(offchannel);
10127 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
10128
10129 /* Alias set topic to set defaulttopic for compatibility. */
10130 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
10131
10132 /* User options */
10133 DEFINE_USER_OPTION(autoinvite);
10134 DEFINE_USER_OPTION(autojoin);
10135 DEFINE_USER_OPTION(info);
10136 DEFINE_USER_OPTION(autoop);
10137
10138 /* Alias uset autovoice to uset autoop. */
10139 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
10140
10141 note_types = dict_new();
10142 dict_set_free_data(note_types, chanserv_deref_note_type);
10143 if(nick)
10144 {
10145 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
10146 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
10147 service_register(chanserv)->trigger = '!';
10148 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check, NULL);
10149 }
10150
10151 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
10152
10153 if(chanserv_conf.channel_expire_frequency)
10154 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
10155
10156 if(chanserv_conf.dnr_expire_frequency)
10157 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
10158
10159 if(chanserv_conf.ban_timeout_frequency)
10160 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
10161
10162 if(chanserv_conf.refresh_period)
10163 {
10164 time_t next_refresh;
10165 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
10166 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
10167 timeq_add(next_refresh, chanserv_auto_resync, NULL);
10168 }
10169
10170 if (autojoin_channels && chanserv) {
10171 for (i = 0; i < autojoin_channels->used; i++) {
10172 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
10173 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
10174 }
10175 }
10176
10177 reg_exit_func(chanserv_db_cleanup, NULL);
10178 message_register_table(msgtab);
10179 }
10180