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