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