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