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