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