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