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