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