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