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