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