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