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