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