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