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