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