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