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