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