1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
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.
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.
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.
25 #include "opserv.h" /* for opserv_bad_channel() and devnull management */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
61 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
62 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
63 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
65 /* ChanServ database */
66 #define KEY_CHANNELS "channels"
67 #define KEY_NOTE_TYPES "note_types"
69 /* Note type parameters */
70 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
71 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
72 #define KEY_NOTE_SETTER_ACCESS "setter_access"
73 #define KEY_NOTE_VISIBILITY "visibility"
74 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
75 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
76 #define KEY_NOTE_VIS_ALL "all"
77 #define KEY_NOTE_MAX_LENGTH "max_length"
78 #define KEY_NOTE_SETTER "setter"
79 #define KEY_NOTE_NOTE "note"
81 /* Do-not-register channels */
83 #define KEY_DNR_SET "set"
84 #define KEY_DNR_SETTER "setter"
85 #define KEY_DNR_REASON "reason"
88 #define KEY_REGISTERED "registered"
89 #define KEY_REGISTRAR "registrar"
90 #define KEY_SUSPENDED "suspended"
91 #define KEY_PREVIOUS "previous"
92 #define KEY_SUSPENDER "suspender"
93 #define KEY_ISSUED "issued"
94 #define KEY_REVOKED "revoked"
95 #define KEY_SUSPEND_EXPIRES "suspend_expires"
96 #define KEY_SUSPEND_REASON "suspend_reason"
97 #define KEY_VISITED "visited"
98 #define KEY_TOPIC "topic"
99 #define KEY_GREETING "greeting"
100 #define KEY_USER_GREETING "user_greeting"
101 #define KEY_MODES "modes"
102 #define KEY_FLAGS "flags"
103 #define KEY_OPTIONS "options"
104 #define KEY_USERS "users"
105 #define KEY_BANS "bans"
106 #define KEY_MAX "max"
107 #define KEY_MAX_TIME "max_time"
108 #define KEY_NOTES "notes"
109 #define KEY_TOPIC_MASK "topic_mask"
110 #define KEY_OWNER_TRANSFER "owner_transfer"
111 #define KEY_EXPIRE "expire"
114 #define KEY_LEVEL "level"
115 #define KEY_INFO "info"
116 #define KEY_SEEN "seen"
119 #define KEY_VOTE "vote"
120 #define KEY_VOTE_START "votestart"
121 #define KEY_VOTE_OPTIONS "voptions"
122 #define KEY_VOTE_OPTION_NAME "voptionname"
123 #define KEY_VOTE_VOTED "vvoted"
124 #define KEY_VOTE_VOTEDFOR "vvotefor"
125 #define KEY_VOTE_OPTION_ID "voptionid"
126 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
129 #define KEY_OWNER "owner"
130 #define KEY_REASON "reason"
131 #define KEY_SET "set"
132 #define KEY_DURATION "duration"
133 #define KEY_EXPIRES "expires"
134 #define KEY_TRIGGERED "triggered"
136 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
137 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
138 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
140 /* Administrative messages */
141 static const struct message_entry msgtab[] = {
142 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
144 /* Channel registration */
145 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
146 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
147 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
148 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
149 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
150 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
152 /* Do-not-register channels */
153 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
154 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
155 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
156 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
157 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
158 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
159 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
160 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
161 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
162 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
163 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
164 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
165 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
166 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
168 /* Channel unregistration */
169 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
170 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
171 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
172 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
175 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
176 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
178 /* Channel merging */
179 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
180 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
181 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
182 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
183 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
185 /* Handle unregistration */
186 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
189 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
190 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
191 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
192 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
193 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
194 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
195 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
196 { "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." },
197 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
198 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
199 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
200 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
201 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
202 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
204 /* Removing yourself from a channel. */
205 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
206 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
207 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
209 /* User management */
210 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
211 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
212 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
213 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
214 { "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." },
215 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
216 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
217 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
219 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
220 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
221 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
222 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
223 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
224 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
225 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
228 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
229 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
230 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
231 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
232 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
233 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
234 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
235 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
236 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
237 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
238 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
239 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
240 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
241 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
242 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
243 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
245 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
247 /* Channel management */
248 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
249 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
250 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
252 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
253 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
254 { "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" },
255 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
256 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
257 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
258 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
260 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
261 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
262 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
263 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
264 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
265 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
266 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
267 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
268 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
269 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
270 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
271 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
272 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
273 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
274 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
275 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
276 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
277 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
278 { "CSMSG_SET_MODES", "$bModes $b %s" },
279 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
280 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
281 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
282 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
283 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
284 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
285 { "CSMSG_SET_VOTE", "$bVote $b %d" },
286 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
287 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
288 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
289 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
290 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
291 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
292 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
293 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
294 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
295 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
296 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
297 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
298 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
299 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
300 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
301 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
302 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
303 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
304 { "CSMSG_USET_INFO", "$bInfo $b %s" },
306 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
307 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
308 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
309 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
312 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
313 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
314 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
315 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
316 { "CSMSG_PROTECT_NONE", "No users will be protected." },
317 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
318 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
319 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
320 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
321 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
322 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
323 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
324 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
325 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
326 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
327 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
330 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
331 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
332 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
333 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
334 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
335 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
336 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
337 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
339 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
340 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
341 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
343 /* Channel userlist */
344 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
345 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
346 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
347 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
348 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
350 /* Channel note list */
351 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
352 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
353 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
354 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
355 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
356 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
357 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
358 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
359 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
360 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
361 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
362 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
363 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
364 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
365 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
367 /* Channel [un]suspension */
368 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
369 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
370 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
371 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
372 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
373 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
374 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
376 /* Access information */
377 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
378 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
379 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
380 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
381 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
382 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
383 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
384 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
385 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
386 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
387 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
388 { "CSMSG_UC_H_TITLE", "network helper" },
389 { "CSMSG_LC_H_TITLE", "support helper" },
390 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
391 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
392 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
395 /* Seen information */
396 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
397 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
398 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
399 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
401 /* Names information */
402 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
403 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
405 /* Channel information */
406 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
407 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
408 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
409 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
410 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
411 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
412 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
413 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
414 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
415 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
416 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
417 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
418 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
423 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
424 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
425 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
426 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
427 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
429 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
430 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
431 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
432 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
433 { "CSMSG_PEEK_OPS", "$bOps:$b" },
434 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
436 /* Network information */
437 { "CSMSG_NETWORK_INFO", "Network Information:" },
438 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
439 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
440 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
441 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
442 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
443 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
444 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
445 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
448 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
449 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
450 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
452 /* Channel searches */
453 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
454 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
455 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
456 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
458 /* Channel configuration */
459 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
460 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
461 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
462 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
465 { "CSMSG_USER_OPTIONS", "User Options:" },
466 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
469 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
470 { "CSMSG_PING_RESPONSE", "Pong!" },
471 { "CSMSG_WUT_RESPONSE", "wut" },
472 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
473 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
474 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
475 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
476 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
477 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
478 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
481 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
482 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
483 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
484 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
485 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
486 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
487 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
488 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
489 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
490 { "CSMSG_VOTE_QUESTION", "Question: %s" },
491 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
492 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
493 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
494 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
495 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
496 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
497 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
498 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
499 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
500 { "CSMSG_VOTE_VOTED", "You have already voted." },
501 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
502 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
503 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
504 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
507 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
511 /* eject_user and unban_user flags */
512 #define ACTION_KICK 0x0001
513 #define ACTION_BAN 0x0002
514 #define ACTION_ADD_BAN 0x0004
515 #define ACTION_ADD_TIMED_BAN 0x0008
516 #define ACTION_UNBAN 0x0010
517 #define ACTION_DEL_BAN 0x0020
519 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
520 #define MODELEN 40 + KEYLEN
524 #define CSFUNC_ARGS user, channel, argc, argv, cmd
526 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
527 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
528 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
529 reply("MSG_MISSING_PARAMS", argv[0]); \
533 DECLARE_LIST(dnrList, struct do_not_register *);
534 DEFINE_LIST(dnrList, struct do_not_register *)
536 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
538 struct userNode *chanserv;
541 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
542 static struct log_type *CS_LOG;
546 struct channelList support_channels;
547 struct mod_chanmode default_modes;
549 unsigned long db_backup_frequency;
550 unsigned long channel_expire_frequency;
551 unsigned long dnr_expire_frequency;
553 unsigned long invited_timeout;
555 unsigned long info_delay;
556 unsigned long adjust_delay;
557 unsigned long channel_expire_delay;
558 unsigned int nodelete_level;
560 unsigned int adjust_threshold;
561 int join_flood_threshold;
563 unsigned int greeting_length;
564 unsigned int refresh_period;
565 unsigned int giveownership_period;
567 unsigned int max_owned;
568 unsigned int max_chan_users;
569 unsigned int max_chan_bans;
570 unsigned int max_userinfo_length;
572 struct string_list *set_shows;
573 struct string_list *eightball;
574 struct string_list *old_ban_names;
576 const char *ctcp_short_ban_duration;
577 const char *ctcp_long_ban_duration;
579 const char *irc_operator_epithet;
580 const char *network_helper_epithet;
581 const char *support_helper_epithet;
583 const char *new_channel_authed;
584 const char *new_channel_unauthed;
585 const char *new_channel_msg;
590 struct userNode *user;
591 struct userNode *bot;
592 struct chanNode *channel;
594 unsigned short lowest;
595 unsigned short highest;
596 struct userData **users;
597 struct helpfile_table table;
602 struct userNode *user;
603 struct chanNode *chan;
606 enum note_access_type
608 NOTE_SET_CHANNEL_ACCESS,
609 NOTE_SET_CHANNEL_SETTER,
613 enum note_visible_type
616 NOTE_VIS_CHANNEL_USERS,
622 enum note_access_type set_access_type;
624 unsigned int min_opserv;
625 unsigned short min_ulevel;
627 enum note_visible_type visible_type;
628 unsigned int max_length;
635 struct note_type *type;
636 char setter[NICKSERV_HANDLE_LEN+1];
640 static unsigned int registered_channels;
641 static unsigned int banCount;
643 static const struct {
646 unsigned short level;
649 { "peon", "Peon", UL_PEON, '+' },
650 { "op", "Op", UL_OP, '@' },
651 { "master", "Master", UL_MASTER, '%' },
652 { "coowner", "Coowner", UL_COOWNER, '*' },
653 { "owner", "Owner", UL_OWNER, '!' },
654 { "helper", "BUG:", UL_HELPER, 'X' }
657 static const struct {
660 unsigned short default_value;
661 unsigned int old_idx;
662 unsigned int old_flag;
663 unsigned short flag_value;
665 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
666 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
667 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
668 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
669 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
670 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
671 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
672 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
673 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
674 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
675 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
676 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
679 struct charOptionValues {
682 } protectValues[] = {
683 { 'a', "CSMSG_PROTECT_ALL" },
684 { 'e', "CSMSG_PROTECT_EQUAL" },
685 { 'l', "CSMSG_PROTECT_LOWER" },
686 { 'n', "CSMSG_PROTECT_NONE" }
688 { 'd', "CSMSG_TOYS_DISABLED" },
689 { 'n', "CSMSG_TOYS_PRIVATE" },
690 { 'p', "CSMSG_TOYS_PUBLIC" }
691 }, topicRefreshValues[] = {
692 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
693 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
694 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
695 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
696 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
697 }, ctcpReactionValues[] = {
698 { 'k', "CSMSG_CTCPREACTION_KICK" },
699 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
700 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
701 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
704 static const struct {
708 unsigned int old_idx;
710 struct charOptionValues *values;
712 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
713 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
714 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
715 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
718 struct userData *helperList;
719 struct chanData *channelList;
720 static struct module *chanserv_module;
721 static unsigned int userCount;
723 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
724 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
725 static void unregister_channel(struct chanData *channel, const char *reason);
728 user_level_from_name(const char *name, unsigned short clamp_level)
730 unsigned int level = 0, ii;
732 level = strtoul(name, NULL, 10);
733 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
734 if(!irccasecmp(name, accessLevels[ii].name))
735 level = accessLevels[ii].level;
736 if(level > clamp_level)
742 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
745 *minl = strtoul(arg, &sep, 10);
753 *maxl = strtoul(sep+1, &sep, 10);
761 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
763 struct userData *uData, **head;
765 if(!channel || !handle)
768 if(override && HANDLE_FLAGGED(handle, HELPING)
769 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
771 for(uData = helperList;
772 uData && uData->handle != handle;
773 uData = uData->next);
777 uData = calloc(1, sizeof(struct userData));
778 uData->handle = handle;
780 uData->access = UL_HELPER;
786 uData->next = helperList;
788 helperList->prev = uData;
796 for(uData = channel->users; uData; uData = uData->next)
797 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
800 head = &(channel->users);
803 if(uData && (uData != *head))
805 /* Shuffle the user to the head of whatever list he was in. */
807 uData->next->prev = uData->prev;
809 uData->prev->next = uData->next;
815 (**head).prev = uData;
822 /* Returns non-zero if user has at least the minimum access.
823 * exempt_owner is set when handling !set, so the owner can set things
826 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
828 struct userData *uData;
829 struct chanData *cData = channel->channel_info;
830 unsigned short minimum = cData->lvlOpts[opt];
833 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
836 if(minimum <= uData->access)
838 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
843 /* Scan for other users authenticated to the same handle
844 still in the channel. If so, keep them listed as present.
846 user is optional, if not null, it skips checking that userNode
847 (for the handle_part function) */
849 scan_user_presence(struct userData *uData, struct userNode *user)
853 if(IsSuspended(uData->channel)
854 || IsUserSuspended(uData)
855 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
867 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
869 unsigned int eflags, argc;
871 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
873 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
874 if(!channel->channel_info
875 || IsSuspended(channel->channel_info)
877 || !ircncasecmp(text, "ACTION ", 7))
879 /* Figure out the minimum level needed to CTCP the channel */
880 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
882 /* We need to enforce against them; do so. */
884 argv[0] = (char*)text;
885 argv[1] = user->nick;
887 if(GetUserMode(channel, user))
888 eflags |= ACTION_KICK;
889 switch(channel->channel_info->chOpts[chCTCPReaction]) {
890 default: case 'k': /* just do the kick */ break;
892 eflags |= ACTION_BAN;
895 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
896 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
899 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
900 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
903 argv[argc++] = bad_ctcp_reason;
904 eject_user(chanserv, channel, argc, argv, NULL, eflags);
908 chanserv_create_note_type(const char *name)
910 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
911 strcpy(ntype->name, name);
913 dict_insert(note_types, ntype->name, ntype);
918 free_vote_options(void *data)
920 struct vote_option *vOpt = data;
922 free(vOpt->option_str);
927 chanserv_deref_note_type(void *data)
929 struct note_type *ntype = data;
931 if(--ntype->refs > 0)
937 chanserv_flush_note_type(struct note_type *ntype)
939 struct chanData *cData;
940 for(cData = channelList; cData; cData = cData->next)
941 dict_remove(cData->notes, ntype->name);
945 chanserv_truncate_notes(struct note_type *ntype)
947 struct chanData *cData;
949 unsigned int size = sizeof(*note) + ntype->max_length;
951 for(cData = channelList; cData; cData = cData->next) {
952 note = dict_find(cData->notes, ntype->name, NULL);
955 if(strlen(note->note) <= ntype->max_length)
957 dict_remove2(cData->notes, ntype->name, 1);
958 note = realloc(note, size);
959 note->note[ntype->max_length] = 0;
960 dict_insert(cData->notes, ntype->name, note);
964 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
967 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
970 unsigned int len = strlen(text);
972 if(len > type->max_length) len = type->max_length;
973 note = calloc(1, sizeof(*note) + len);
975 strncpy(note->setter, setter, sizeof(note->setter)-1);
976 memcpy(note->note, text, len);
978 dict_insert(channel->notes, type->name, note);
984 chanserv_free_note(void *data)
986 struct note *note = data;
988 chanserv_deref_note_type(note->type);
989 assert(note->type->refs > 0); /* must use delnote to remove the type */
993 static MODCMD_FUNC(cmd_createnote) {
994 struct note_type *ntype;
995 unsigned int arg = 1, existed = 0, max_length;
997 if((ntype = dict_find(note_types, argv[1], NULL)))
1000 ntype = chanserv_create_note_type(argv[arg]);
1001 if(!irccasecmp(argv[++arg], "privileged"))
1004 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1005 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1007 else if(!irccasecmp(argv[arg], "channel"))
1009 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1012 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1015 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1016 ntype->set_access.min_ulevel = ulvl;
1018 else if(!irccasecmp(argv[arg], "setter"))
1020 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1024 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1028 if(!irccasecmp(argv[++arg], "privileged"))
1029 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1030 else if(!irccasecmp(argv[arg], "channel_users"))
1031 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1032 else if(!irccasecmp(argv[arg], "all"))
1033 ntype->visible_type = NOTE_VIS_ALL;
1035 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1039 if((arg+1) >= argc) {
1040 reply("MSG_MISSING_PARAMS", argv[0]);
1043 max_length = strtoul(argv[++arg], NULL, 0);
1044 if(max_length < 20 || max_length > 450)
1046 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1049 if(existed && (max_length < ntype->max_length))
1051 ntype->max_length = max_length;
1052 chanserv_truncate_notes(ntype);
1054 ntype->max_length = max_length;
1057 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1059 reply("CSMSG_NOTE_CREATED", ntype->name);
1064 dict_remove(note_types, ntype->name);
1068 static MODCMD_FUNC(cmd_removenote) {
1069 struct note_type *ntype;
1072 ntype = dict_find(note_types, argv[1], NULL);
1073 force = (argc > 2) && !irccasecmp(argv[2], "force");
1076 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1083 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1086 chanserv_flush_note_type(ntype);
1088 dict_remove(note_types, argv[1]);
1089 reply("CSMSG_NOTE_DELETED", argv[1]);
1094 chanserv_expire_channel(void *data)
1096 struct chanData *channel = data;
1097 char reason[MAXLEN];
1098 sprintf(reason, "channel expired.");
1099 channel->expiry = 0;
1100 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1101 unregister_channel(channel, reason);
1104 static MODCMD_FUNC(chan_opt_expire)
1106 struct chanData *cData = channel->channel_info;
1107 unsigned long value = cData->expiry;
1111 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1113 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1116 unsigned long expiry,duration;
1118 /* The two directions can have different ACLs. */
1119 if(!strcmp(argv[1], "0"))
1121 else if((duration = ParseInterval(argv[1])))
1122 expiry = now + duration;
1125 reply("MSG_INVALID_DURATION", argv[1]);
1129 if (expiry != value)
1133 timeq_del(value, chanserv_expire_channel, cData, 0);
1136 cData->expiry = value;
1139 timeq_add(expiry, chanserv_expire_channel, cData);
1144 if(cData->expiry > now) {
1145 char expirestr[INTERVALLEN];
1146 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1148 reply("CSMSG_SET_EXPIRE_OFF");
1153 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1157 if(orig->modes_set & change->modes_clear)
1159 if(orig->modes_clear & change->modes_set)
1161 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1162 && strcmp(orig->new_key, change->new_key))
1164 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1165 && (orig->new_limit != change->new_limit))
1170 static char max_length_text[MAXLEN+1][16];
1172 static struct helpfile_expansion
1173 chanserv_expand_variable(const char *variable)
1175 struct helpfile_expansion exp;
1177 if(!irccasecmp(variable, "notes"))
1180 exp.type = HF_TABLE;
1181 exp.value.table.length = 1;
1182 exp.value.table.width = 3;
1183 exp.value.table.flags = 0;
1184 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1185 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1186 exp.value.table.contents[0][0] = "Note Type";
1187 exp.value.table.contents[0][1] = "Visibility";
1188 exp.value.table.contents[0][2] = "Max Length";
1189 for(it=dict_first(note_types); it; it=iter_next(it))
1191 struct note_type *ntype = iter_data(it);
1194 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1195 row = exp.value.table.length++;
1196 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1197 exp.value.table.contents[row][0] = ntype->name;
1198 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1199 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1201 if(!max_length_text[ntype->max_length][0])
1202 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1203 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1208 exp.type = HF_STRING;
1209 exp.value.str = NULL;
1213 static struct chanData*
1214 register_channel(struct chanNode *cNode, char *registrar)
1216 struct chanData *channel;
1217 enum levelOption lvlOpt;
1218 enum charOption chOpt;
1220 channel = calloc(1, sizeof(struct chanData));
1222 channel->notes = dict_new();
1223 dict_set_free_data(channel->notes, chanserv_free_note);
1225 channel->registrar = strdup(registrar);
1226 channel->registered = now;
1227 channel->visited = now;
1228 channel->limitAdjusted = now;
1229 channel->ownerTransfer = now;
1230 channel->flags = CHANNEL_DEFAULT_FLAGS;
1231 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1232 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1233 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1234 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1236 channel->prev = NULL;
1237 channel->next = channelList;
1240 channelList->prev = channel;
1241 channelList = channel;
1242 registered_channels++;
1244 channel->channel = cNode;
1246 cNode->channel_info = channel;
1248 channel->vote = NULL;
1253 static struct userData*
1254 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1256 struct userData *ud;
1258 if(access_level > UL_OWNER)
1261 ud = calloc(1, sizeof(*ud));
1262 ud->channel = channel;
1263 ud->handle = handle;
1265 ud->access = access_level;
1266 ud->info = info ? strdup(info) : NULL;
1269 ud->next = channel->users;
1271 channel->users->prev = ud;
1272 channel->users = ud;
1274 channel->userCount++;
1278 ud->u_next = ud->handle->channels;
1280 ud->u_next->u_prev = ud;
1281 ud->handle->channels = ud;
1287 del_channel_user(struct userData *user, int do_gc)
1289 struct chanData *channel = user->channel;
1291 channel->userCount--;
1295 user->prev->next = user->next;
1297 channel->users = user->next;
1299 user->next->prev = user->prev;
1302 user->u_prev->u_next = user->u_next;
1304 user->handle->channels = user->u_next;
1306 user->u_next->u_prev = user->u_prev;
1310 if(do_gc && !channel->users && !IsProtected(channel)) {
1311 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1312 unregister_channel(channel, "lost all users.");
1316 static void expire_ban(void *data);
1319 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1322 unsigned int ii, l1, l2;
1327 bd = malloc(sizeof(struct banData));
1329 bd->channel = channel;
1331 bd->triggered = triggered;
1332 bd->expires = expires;
1334 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1336 extern const char *hidden_host_suffix;
1337 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1341 l2 = strlen(old_name);
1344 if(irccasecmp(mask + l1 - l2, old_name))
1346 new_mask = alloca(MAXLEN);
1347 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1350 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1352 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1353 bd->reason = strdup(reason);
1356 timeq_add(expires, expire_ban, bd);
1359 bd->next = channel->bans;
1361 channel->bans->prev = bd;
1363 channel->banCount++;
1370 del_channel_ban(struct banData *ban)
1372 ban->channel->banCount--;
1376 ban->prev->next = ban->next;
1378 ban->channel->bans = ban->next;
1381 ban->next->prev = ban->prev;
1384 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1393 expire_ban(void *data)
1395 struct banData *bd = data;
1396 if(!IsSuspended(bd->channel))
1398 struct banList bans;
1399 struct mod_chanmode change;
1401 bans = bd->channel->channel->banlist;
1402 mod_chanmode_init(&change);
1403 for(ii=0; ii<bans.used; ii++)
1405 if(!strcmp(bans.list[ii]->ban, bd->mask))
1408 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1409 change.args[0].u.hostmask = bd->mask;
1410 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1416 del_channel_ban(bd);
1419 static void chanserv_expire_suspension(void *data);
1422 unregister_channel(struct chanData *channel, const char *reason)
1424 struct mod_chanmode change;
1425 char msgbuf[MAXLEN];
1427 /* After channel unregistration, the following must be cleaned
1429 - Channel information.
1432 - Channel suspension data.
1433 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1439 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1443 mod_chanmode_init(&change);
1444 change.modes_clear |= MODE_REGISTERED;
1445 mod_chanmode_announce(chanserv, channel->channel, &change);
1448 while(channel->users)
1449 del_channel_user(channel->users, 0);
1451 while(channel->bans)
1452 del_channel_ban(channel->bans);
1454 free(channel->topic);
1455 free(channel->registrar);
1456 free(channel->greeting);
1457 free(channel->user_greeting);
1458 free(channel->topic_mask);
1461 channel->prev->next = channel->next;
1463 channelList = channel->next;
1466 channel->next->prev = channel->prev;
1468 if(channel->suspended)
1470 struct chanNode *cNode = channel->channel;
1471 struct suspended *suspended, *next_suspended;
1473 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1475 next_suspended = suspended->previous;
1476 free(suspended->suspender);
1477 free(suspended->reason);
1478 if(suspended->expires)
1479 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1484 cNode->channel_info = NULL;
1487 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1488 channel->channel->channel_info = NULL;
1490 dict_delete(channel->notes);
1491 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1492 if(!IsSuspended(channel))
1493 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1494 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1495 UnlockChannel(channel->channel);
1497 registered_channels--;
1501 expire_channels(UNUSED_ARG(void *data))
1503 struct chanData *channel, *next;
1504 struct userData *user;
1505 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1507 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1508 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1510 for(channel = channelList; channel; channel = next)
1512 next = channel->next;
1514 /* See if the channel can be expired. */
1515 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1516 || IsProtected(channel))
1519 /* Make sure there are no high-ranking users still in the channel. */
1520 for(user=channel->users; user; user=user->next)
1521 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1526 /* Unregister the channel */
1527 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1528 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1529 unregister_channel(channel, "registration expired.");
1532 if(chanserv_conf.channel_expire_frequency)
1533 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1537 expire_dnrs(UNUSED_ARG(void *data))
1539 dict_iterator_t it, next;
1540 struct do_not_register *dnr;
1542 for(it = dict_first(handle_dnrs); it; it = next)
1544 dnr = iter_data(it);
1545 next = iter_next(it);
1546 if(dnr->expires && dnr->expires <= now)
1547 dict_remove(handle_dnrs, dnr->chan_name + 1);
1549 for(it = dict_first(plain_dnrs); it; it = next)
1551 dnr = iter_data(it);
1552 next = iter_next(it);
1553 if(dnr->expires && dnr->expires <= now)
1554 dict_remove(plain_dnrs, dnr->chan_name + 1);
1556 for(it = dict_first(mask_dnrs); it; it = next)
1558 dnr = iter_data(it);
1559 next = iter_next(it);
1560 if(dnr->expires && dnr->expires <= now)
1561 dict_remove(mask_dnrs, dnr->chan_name + 1);
1564 if(chanserv_conf.dnr_expire_frequency)
1565 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1569 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1571 char protect = channel->chOpts[chProtect];
1572 struct userData *cs_victim, *cs_aggressor;
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 || IsOper(aggressor))
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
1582 cs_victim = GetChannelAccess(channel, victim->handle_info);
1583 if(protect != 'a' && !cs_victim)
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);
1592 /* If the aggressor was a user, then the victim can't be helped. */
1599 if(cs_victim->access > cs_aggressor->access)
1604 if(cs_victim->access >= cs_aggressor->access)
1613 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1615 struct chanData *cData = channel->channel_info;
1616 struct userData *cs_victim;
1618 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1619 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1620 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1622 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1630 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1632 if(IsService(victim))
1634 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1638 if(protect_user(victim, user, channel->channel_info))
1640 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1647 static struct do_not_register *
1648 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1650 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1651 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1652 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1653 strcpy(dnr->reason, reason);
1655 dnr->expires = expires;
1656 if(dnr->chan_name[0] == '*')
1657 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1658 else if(strpbrk(dnr->chan_name, "*?"))
1659 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1661 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1665 static struct dnrList
1666 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1668 struct dnrList list;
1669 dict_iterator_t it, next;
1670 struct do_not_register *dnr;
1672 dnrList_init(&list);
1674 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1676 if(dnr->expires && dnr->expires <= now)
1677 dict_remove(handle_dnrs, handle);
1678 else if(list.used < max)
1679 dnrList_append(&list, dnr);
1682 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1684 if(dnr->expires && dnr->expires <= now)
1685 dict_remove(plain_dnrs, chan_name);
1686 else if(list.used < max)
1687 dnrList_append(&list, dnr);
1692 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1694 next = iter_next(it);
1695 if(!match_ircglob(chan_name, iter_key(it)))
1697 dnr = iter_data(it);
1698 if(dnr->expires && dnr->expires <= now)
1699 dict_remove(mask_dnrs, iter_key(it));
1701 dnrList_append(&list, dnr);
1708 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1710 struct userNode *user;
1711 char buf1[INTERVALLEN];
1712 char buf2[INTERVALLEN];
1719 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1724 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1725 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1729 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1732 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1737 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1739 struct dnrList list;
1742 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1743 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1744 dnr_print_func(list.list[ii], user);
1746 reply("CSMSG_MORE_DNRS", list.used - ii);
1751 struct do_not_register *
1752 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1754 struct dnrList list;
1755 struct do_not_register *dnr;
1757 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1758 dnr = list.used ? list.list[0] : NULL;
1763 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1765 struct do_not_register *dnr;
1766 dict_iterator_t it, next;
1767 unsigned int matches = 0;
1769 for(it = dict_first(dict); it; it = next)
1771 dnr = iter_data(it);
1772 next = iter_next(it);
1773 if(dnr->expires && dnr->expires <= now)
1775 dict_remove(dict, iter_key(it));
1778 dnr_print_func(dnr, user);
1785 static CHANSERV_FUNC(cmd_noregister)
1789 unsigned long expiry, duration;
1790 unsigned int matches;
1794 reply("CSMSG_DNR_SEARCH_RESULTS");
1795 matches = send_dnrs(user, handle_dnrs);
1796 matches += send_dnrs(user, plain_dnrs);
1797 matches += send_dnrs(user, mask_dnrs);
1799 reply("MSG_MATCH_COUNT", matches);
1801 reply("MSG_NO_MATCHES");
1807 if(!IsChannelName(target) && (*target != '*'))
1809 reply("CSMSG_NOT_DNR", target);
1817 reply("MSG_INVALID_DURATION", argv[2]);
1821 if(!strcmp(argv[2], "0"))
1823 else if((duration = ParseInterval(argv[2])))
1824 expiry = now + duration;
1827 reply("MSG_INVALID_DURATION", argv[2]);
1831 reason = unsplit_string(argv + 3, argc - 3, NULL);
1832 if((*target == '*') && !get_handle_info(target + 1))
1834 reply("MSG_HANDLE_UNKNOWN", target + 1);
1837 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1838 reply("CSMSG_NOREGISTER_CHANNEL", target);
1842 reply("CSMSG_DNR_SEARCH_RESULTS");
1844 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1846 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1848 reply("MSG_NO_MATCHES");
1852 static CHANSERV_FUNC(cmd_allowregister)
1854 const char *chan_name = argv[1];
1856 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1857 || dict_remove(plain_dnrs, chan_name)
1858 || dict_remove(mask_dnrs, chan_name))
1860 reply("CSMSG_DNR_REMOVED", chan_name);
1863 reply("CSMSG_NO_SUCH_DNR", chan_name);
1868 struct userNode *source;
1872 unsigned long min_set, max_set;
1873 unsigned long min_expires, max_expires;
1878 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1880 return !((dnr->set < search->min_set)
1881 || (dnr->set > search->max_set)
1882 || (dnr->expires < search->min_expires)
1883 || (search->max_expires
1884 && ((dnr->expires == 0)
1885 || (dnr->expires > search->max_expires)))
1886 || (search->chan_mask
1887 && !match_ircglob(dnr->chan_name, search->chan_mask))
1888 || (search->setter_mask
1889 && !match_ircglob(dnr->setter, search->setter_mask))
1890 || (search->reason_mask
1891 && !match_ircglob(dnr->reason, search->reason_mask)));
1894 static struct dnr_search *
1895 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1897 struct dnr_search *discrim;
1900 discrim = calloc(1, sizeof(*discrim));
1901 discrim->source = user;
1902 discrim->chan_mask = NULL;
1903 discrim->setter_mask = NULL;
1904 discrim->reason_mask = NULL;
1905 discrim->max_set = INT_MAX;
1906 discrim->limit = 50;
1908 for(ii=0; ii<argc; ++ii)
1912 reply("MSG_MISSING_PARAMS", argv[ii]);
1915 else if(0 == irccasecmp(argv[ii], "channel"))
1917 discrim->chan_mask = argv[++ii];
1919 else if(0 == irccasecmp(argv[ii], "setter"))
1921 discrim->setter_mask = argv[++ii];
1923 else if(0 == irccasecmp(argv[ii], "reason"))
1925 discrim->reason_mask = argv[++ii];
1927 else if(0 == irccasecmp(argv[ii], "limit"))
1929 discrim->limit = strtoul(argv[++ii], NULL, 0);
1931 else if(0 == irccasecmp(argv[ii], "set"))
1933 const char *cmp = argv[++ii];
1936 discrim->min_set = now - ParseInterval(cmp + 2);
1938 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1939 } else if(cmp[0] == '=') {
1940 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1941 } else if(cmp[0] == '>') {
1943 discrim->max_set = now - ParseInterval(cmp + 2);
1945 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1947 discrim->max_set = now - (ParseInterval(cmp) - 1);
1950 else if(0 == irccasecmp(argv[ii], "expires"))
1952 const char *cmp = argv[++ii];
1955 discrim->max_expires = now + ParseInterval(cmp + 2);
1957 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1958 } else if(cmp[0] == '=') {
1959 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1960 } else if(cmp[0] == '>') {
1962 discrim->min_expires = now + ParseInterval(cmp + 2);
1964 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1966 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1971 reply("MSG_INVALID_CRITERIA", argv[ii]);
1982 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1985 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1987 struct do_not_register *dnr;
1988 dict_iterator_t next;
1993 /* Initialize local variables. */
1996 if(discrim->chan_mask)
1998 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1999 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2003 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2005 /* Check against account-based DNRs. */
2006 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2007 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 else if(target_fixed)
2012 /* Check against channel-based DNRs. */
2013 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2014 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2019 /* Exhaustively search account DNRs. */
2020 for(it = dict_first(handle_dnrs); it; it = next)
2022 next = iter_next(it);
2023 dnr = iter_data(it);
2024 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2028 /* Do the same for channel DNRs. */
2029 for(it = dict_first(plain_dnrs); it; it = next)
2031 next = iter_next(it);
2032 dnr = iter_data(it);
2033 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2037 /* Do the same for wildcarded channel DNRs. */
2038 for(it = dict_first(mask_dnrs); it; it = next)
2040 next = iter_next(it);
2041 dnr = iter_data(it);
2042 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2050 dnr_remove_func(struct do_not_register *match, void *extra)
2052 struct userNode *user;
2055 chan_name = alloca(strlen(match->chan_name) + 1);
2056 strcpy(chan_name, match->chan_name);
2058 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2059 || dict_remove(plain_dnrs, chan_name)
2060 || dict_remove(mask_dnrs, chan_name))
2062 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2068 dnr_count_func(struct do_not_register *match, void *extra)
2070 return 0; (void)match; (void)extra;
2073 static MODCMD_FUNC(cmd_dnrsearch)
2075 struct dnr_search *discrim;
2076 dnr_search_func action;
2077 struct svccmd *subcmd;
2078 unsigned int matches;
2081 sprintf(buf, "dnrsearch %s", argv[1]);
2082 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2085 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2088 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2090 if(!irccasecmp(argv[1], "print"))
2091 action = dnr_print_func;
2092 else if(!irccasecmp(argv[1], "remove"))
2093 action = dnr_remove_func;
2094 else if(!irccasecmp(argv[1], "count"))
2095 action = dnr_count_func;
2098 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2102 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2106 if(action == dnr_print_func)
2107 reply("CSMSG_DNR_SEARCH_RESULTS");
2108 matches = dnr_search(discrim, action, user);
2110 reply("MSG_MATCH_COUNT", matches);
2112 reply("MSG_NO_MATCHES");
2118 chanserv_get_owned_count(struct handle_info *hi)
2120 struct userData *cList;
2123 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2124 if(cList->access == UL_OWNER)
2129 static CHANSERV_FUNC(cmd_register)
2131 struct handle_info *handle;
2132 struct chanData *cData;
2133 struct modeNode *mn;
2134 char reason[MAXLEN];
2136 unsigned int new_channel, force=0;
2137 struct do_not_register *dnr;
2141 if(channel->channel_info)
2143 reply("CSMSG_ALREADY_REGGED", channel->name);
2147 if(channel->bad_channel)
2149 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2154 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2156 reply("CSMSG_MUST_BE_OPPED", channel->name);
2161 chan_name = channel->name;
2165 if((argc < 2) || !IsChannelName(argv[1]))
2167 reply("MSG_NOT_CHANNEL_NAME");
2171 if(opserv_bad_channel(argv[1]))
2173 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2178 chan_name = argv[1];
2181 if(argc >= (new_channel+2))
2183 if(!IsHelping(user))
2185 reply("CSMSG_PROXY_FORBIDDEN");
2189 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2191 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2192 dnr = chanserv_is_dnr(chan_name, handle);
2196 handle = user->handle_info;
2197 dnr = chanserv_is_dnr(chan_name, handle);
2201 if(!IsHelping(user))
2202 reply("CSMSG_DNR_CHANNEL", chan_name);
2204 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2208 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2210 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2215 channel = AddChannel(argv[1], now, NULL, NULL);
2217 cData = register_channel(channel, user->handle_info->handle);
2218 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2219 cData->modes = chanserv_conf.default_modes;
2221 cData->modes.modes_set |= MODE_REGISTERED;
2222 if (IsOffChannel(cData))
2224 mod_chanmode_announce(chanserv, channel, &cData->modes);
2228 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2229 change->args[change->argc].mode = MODE_CHANOP;
2230 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2232 mod_chanmode_announce(chanserv, channel, change);
2233 mod_chanmode_free(change);
2236 /* Initialize the channel's max user record. */
2237 cData->max = channel->members.used;
2238 cData->max_time = 0;
2240 if(handle != user->handle_info)
2241 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2243 reply("CSMSG_REG_SUCCESS", channel->name);
2245 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2246 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2251 make_confirmation_string(struct userData *uData)
2253 static char strbuf[16];
2258 for(src = uData->handle->handle; *src; )
2259 accum = accum * 31 + toupper(*src++);
2261 for(src = uData->channel->channel->name; *src; )
2262 accum = accum * 31 + toupper(*src++);
2263 sprintf(strbuf, "%08x", accum);
2267 static CHANSERV_FUNC(cmd_unregister)
2270 char reason[MAXLEN];
2271 struct chanData *cData;
2272 struct userData *uData;
2274 cData = channel->channel_info;
2277 reply("CSMSG_NOT_REGISTERED", channel->name);
2281 uData = GetChannelUser(cData, user->handle_info);
2282 if(!uData || (uData->access < UL_OWNER))
2284 reply("CSMSG_NO_ACCESS");
2288 if(IsProtected(cData) && !IsOper(user))
2290 reply("CSMSG_UNREG_NODELETE", channel->name);
2294 if(!IsHelping(user))
2296 const char *confirm_string;
2297 if(IsSuspended(cData))
2299 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2302 confirm_string = make_confirmation_string(uData);
2303 if((argc < 2) || strcmp(argv[1], confirm_string))
2305 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2310 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2311 name = strdup(channel->name);
2312 unregister_channel(cData, reason);
2313 spamserv_cs_unregister(user, channel, manually, "unregistered");
2314 reply("CSMSG_UNREG_SUCCESS", name);
2320 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2322 extern struct userNode *spamserv;
2323 struct mod_chanmode *change;
2325 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2327 change = mod_chanmode_alloc(2);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2331 change->args[1].mode = MODE_CHANOP;
2332 change->args[1].u.member = AddChannelUser(spamserv, channel);
2336 change = mod_chanmode_alloc(1);
2338 change->args[0].mode = MODE_CHANOP;
2339 change->args[0].u.member = AddChannelUser(chanserv, channel);
2342 mod_chanmode_announce(chanserv, channel, change);
2343 mod_chanmode_free(change);
2346 static CHANSERV_FUNC(cmd_move)
2348 struct mod_chanmode change;
2349 struct chanNode *target;
2350 struct modeNode *mn;
2351 struct userData *uData;
2352 char reason[MAXLEN];
2353 struct do_not_register *dnr;
2354 int chanserv_join = 0, spamserv_join;
2358 if(IsProtected(channel->channel_info))
2360 reply("CSMSG_MOVE_NODELETE", channel->name);
2364 if(!IsChannelName(argv[1]))
2366 reply("MSG_NOT_CHANNEL_NAME");
2370 if(opserv_bad_channel(argv[1]))
2372 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2376 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2378 for(uData = channel->channel_info->users; uData; uData = uData->next)
2380 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2382 if(!IsHelping(user))
2383 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2385 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2391 mod_chanmode_init(&change);
2392 if(!(target = GetChannel(argv[1])))
2394 target = AddChannel(argv[1], now, NULL, NULL);
2395 if(!IsSuspended(channel->channel_info))
2398 else if(target->channel_info)
2400 reply("CSMSG_ALREADY_REGGED", target->name);
2403 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2404 && !IsHelping(user))
2406 reply("CSMSG_MUST_BE_OPPED", target->name);
2409 else if(!IsSuspended(channel->channel_info))
2414 /* Clear MODE_REGISTERED from old channel, add it to new. */
2416 change.modes_clear = MODE_REGISTERED;
2417 mod_chanmode_announce(chanserv, channel, &change);
2418 change.modes_clear = 0;
2419 change.modes_set = MODE_REGISTERED;
2420 mod_chanmode_announce(chanserv, target, &change);
2423 /* Move the channel_info to the target channel; it
2424 shouldn't be necessary to clear timeq callbacks
2425 for the old channel. */
2426 target->channel_info = channel->channel_info;
2427 target->channel_info->channel = target;
2428 channel->channel_info = NULL;
2430 /* Check whether users are present in the new channel. */
2431 for(uData = target->channel_info->users; uData; uData = uData->next)
2432 scan_user_presence(uData, NULL);
2434 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2437 ss_cs_join_channel(target, spamserv_join);
2439 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2440 if(!IsSuspended(target->channel_info))
2442 char reason2[MAXLEN];
2443 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2444 DelChannelUser(chanserv, channel, reason2, 0);
2446 UnlockChannel(channel);
2447 LockChannel(target);
2448 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2449 reply("CSMSG_MOVE_SUCCESS", target->name);
2454 merge_users(struct chanData *source, struct chanData *target)
2456 struct userData *suData, *tuData, *next;
2462 /* Insert the source's users into the scratch area. */
2463 for(suData = source->users; suData; suData = suData->next)
2464 dict_insert(merge, suData->handle->handle, suData);
2466 /* Iterate through the target's users, looking for
2467 users common to both channels. The lower access is
2468 removed from either the scratch area or target user
2470 for(tuData = target->users; tuData; tuData = next)
2472 struct userData *choice;
2474 next = tuData->next;
2476 /* If a source user exists with the same handle as a target
2477 channel's user, resolve the conflict by removing one. */
2478 suData = dict_find(merge, tuData->handle->handle, NULL);
2482 /* Pick the data we want to keep. */
2483 /* If the access is the same, use the later seen time. */
2484 if(suData->access == tuData->access)
2485 choice = (suData->seen > tuData->seen) ? suData : tuData;
2486 else /* Otherwise, keep the higher access level. */
2487 choice = (suData->access > tuData->access) ? suData : tuData;
2488 /* Use the later seen time. */
2489 if(suData->seen < tuData->seen)
2490 suData->seen = tuData->seen;
2492 tuData->seen = suData->seen;
2494 /* Remove the user that wasn't picked. */
2495 if(choice == tuData)
2497 dict_remove(merge, suData->handle->handle);
2498 del_channel_user(suData, 0);
2501 del_channel_user(tuData, 0);
2504 /* Move the remaining users to the target channel. */
2505 for(it = dict_first(merge); it; it = iter_next(it))
2507 suData = iter_data(it);
2509 /* Insert the user into the target channel's linked list. */
2510 suData->prev = NULL;
2511 suData->next = target->users;
2512 suData->channel = target;
2515 target->users->prev = suData;
2516 target->users = suData;
2518 /* Update the user counts for the target channel; the
2519 source counts are left alone. */
2520 target->userCount++;
2522 /* Check whether the user is in the target channel. */
2523 scan_user_presence(suData, NULL);
2526 /* Possible to assert (source->users == NULL) here. */
2527 source->users = NULL;
2532 merge_bans(struct chanData *source, struct chanData *target)
2534 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2536 /* Hold on to the original head of the target ban list
2537 to avoid comparing source bans with source bans. */
2538 tFront = target->bans;
2540 /* Perform a totally expensive O(n*m) merge, ick. */
2541 for(sbData = source->bans; sbData; sbData = sNext)
2543 /* Flag to track whether the ban's been moved
2544 to the destination yet. */
2547 /* Possible to assert (sbData->prev == NULL) here. */
2548 sNext = sbData->next;
2550 for(tbData = tFront; tbData; tbData = tNext)
2552 tNext = tbData->next;
2554 /* Perform two comparisons between each source
2555 and target ban, conflicts are resolved by
2556 keeping the broader ban and copying the later
2557 expiration and triggered time. */
2558 if(match_ircglobs(tbData->mask, sbData->mask))
2560 /* There is a broader ban in the target channel that
2561 overrides one in the source channel; remove the
2562 source ban and break. */
2563 if(sbData->expires > tbData->expires)
2564 tbData->expires = sbData->expires;
2565 if(sbData->triggered > tbData->triggered)
2566 tbData->triggered = sbData->triggered;
2567 del_channel_ban(sbData);
2570 else if(match_ircglobs(sbData->mask, tbData->mask))
2572 /* There is a broader ban in the source channel that
2573 overrides one in the target channel; remove the
2574 target ban, fall through and move the source over. */
2575 if(tbData->expires > sbData->expires)
2576 sbData->expires = tbData->expires;
2577 if(tbData->triggered > sbData->triggered)
2578 sbData->triggered = tbData->triggered;
2579 if(tbData == tFront)
2581 del_channel_ban(tbData);
2584 /* Source bans can override multiple target bans, so
2585 we allow a source to run through this loop multiple
2586 times, but we can only move it once. */
2591 /* Remove the source ban from the source ban list. */
2593 sbData->next->prev = sbData->prev;
2595 /* Modify the source ban's associated channel. */
2596 sbData->channel = target;
2598 /* Insert the ban into the target channel's linked list. */
2599 sbData->prev = NULL;
2600 sbData->next = target->bans;
2603 target->bans->prev = sbData;
2604 target->bans = sbData;
2606 /* Update the user counts for the target channel. */
2611 /* Possible to assert (source->bans == NULL) here. */
2612 source->bans = NULL;
2616 merge_data(struct chanData *source, struct chanData *target)
2618 /* Use more recent visited and owner-transfer time; use older
2619 * registered time. Bitwise or may_opchan. Use higher max.
2620 * Do not touch last_refresh, ban count or user counts.
2622 if(source->visited > target->visited)
2623 target->visited = source->visited;
2624 if(source->registered < target->registered)
2625 target->registered = source->registered;
2626 if(source->ownerTransfer > target->ownerTransfer)
2627 target->ownerTransfer = source->ownerTransfer;
2628 if(source->may_opchan)
2629 target->may_opchan = 1;
2630 if(source->max > target->max) {
2631 target->max = source->max;
2632 target->max_time = source->max_time;
2637 merge_channel(struct chanData *source, struct chanData *target)
2639 merge_users(source, target);
2640 merge_bans(source, target);
2641 merge_data(source, target);
2644 static CHANSERV_FUNC(cmd_merge)
2646 struct userData *target_user;
2647 struct chanNode *target;
2648 char reason[MAXLEN];
2652 /* Make sure the target channel exists and is registered to the user
2653 performing the command. */
2654 if(!(target = GetChannel(argv[1])))
2656 reply("MSG_INVALID_CHANNEL");
2660 if(!target->channel_info)
2662 reply("CSMSG_NOT_REGISTERED", target->name);
2666 if(IsProtected(channel->channel_info))
2668 reply("CSMSG_MERGE_NODELETE");
2672 if(IsSuspended(target->channel_info))
2674 reply("CSMSG_MERGE_SUSPENDED");
2678 if(channel == target)
2680 reply("CSMSG_MERGE_SELF");
2684 target_user = GetChannelUser(target->channel_info, user->handle_info);
2685 if(!target_user || (target_user->access < UL_OWNER))
2687 reply("CSMSG_MERGE_NOT_OWNER");
2691 /* Merge the channel structures and associated data. */
2692 merge_channel(channel->channel_info, target->channel_info);
2693 spamserv_cs_move_merge(user, channel, target, 0);
2694 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2695 unregister_channel(channel->channel_info, reason);
2696 reply("CSMSG_MERGE_SUCCESS", target->name);
2700 static CHANSERV_FUNC(cmd_opchan)
2702 struct mod_chanmode change;
2703 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2705 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2708 channel->channel_info->may_opchan = 0;
2709 mod_chanmode_init(&change);
2711 change.args[0].mode = MODE_CHANOP;
2712 change.args[0].u.member = GetUserMode(channel, chanserv);
2713 if(!change.args[0].u.member)
2715 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2718 mod_chanmode_announce(chanserv, channel, &change);
2719 reply("CSMSG_OPCHAN_DONE", channel->name);
2723 static CHANSERV_FUNC(cmd_adduser)
2725 struct userData *actee;
2726 struct userData *actor, *real_actor;
2727 struct handle_info *handle;
2728 unsigned short access_level, override = 0;
2732 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2734 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2738 access_level = user_level_from_name(argv[2], UL_OWNER);
2741 reply("CSMSG_INVALID_ACCESS", argv[2]);
2745 actor = GetChannelUser(channel->channel_info, user->handle_info);
2746 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2748 if(actor->access <= access_level)
2750 reply("CSMSG_NO_BUMP_ACCESS");
2754 /* Trying to add someone with equal/more access? */
2755 if (!real_actor || real_actor->access <= access_level)
2756 override = CMD_LOG_OVERRIDE;
2758 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2761 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2763 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2767 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2768 scan_user_presence(actee, NULL);
2769 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2770 return 1 | override;
2773 static CHANSERV_FUNC(cmd_clvl)
2775 struct handle_info *handle;
2776 struct userData *victim;
2777 struct userData *actor, *real_actor;
2778 unsigned short new_access, override = 0;
2779 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2783 actor = GetChannelUser(channel->channel_info, user->handle_info);
2784 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2786 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2789 if(handle == user->handle_info && !privileged)
2791 reply("CSMSG_NO_SELF_CLVL");
2795 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2797 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2801 if(actor->access <= victim->access && !privileged)
2803 reply("MSG_USER_OUTRANKED", handle->handle);
2807 new_access = user_level_from_name(argv[2], UL_OWNER);
2811 reply("CSMSG_INVALID_ACCESS", argv[2]);
2815 if(new_access >= actor->access && !privileged)
2817 reply("CSMSG_NO_BUMP_ACCESS");
2821 /* Trying to clvl a equal/higher user? */
2822 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2823 override = CMD_LOG_OVERRIDE;
2824 /* Trying to clvl someone to equal/higher access? */
2825 if(!real_actor || new_access >= real_actor->access)
2826 override = CMD_LOG_OVERRIDE;
2827 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2828 * If they lower their own access it's not a big problem.
2831 victim->access = new_access;
2832 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2833 return 1 | override;
2836 static CHANSERV_FUNC(cmd_deluser)
2838 struct handle_info *handle;
2839 struct userData *victim;
2840 struct userData *actor, *real_actor;
2841 unsigned short access_level, override = 0;
2846 actor = GetChannelUser(channel->channel_info, user->handle_info);
2847 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2849 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2852 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2854 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2860 access_level = user_level_from_name(argv[1], UL_OWNER);
2863 reply("CSMSG_INVALID_ACCESS", argv[1]);
2866 if(access_level != victim->access)
2868 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2874 access_level = victim->access;
2877 if((actor->access <= victim->access) && !IsHelping(user))
2879 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2883 /* If people delete themselves it is an override, but they
2884 * could've used deleteme so we don't log it as an override
2886 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2887 override = CMD_LOG_OVERRIDE;
2889 chan_name = strdup(channel->name);
2890 del_channel_user(victim, 1);
2891 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2893 return 1 | override;
2897 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2899 struct userData *actor, *real_actor, *uData, *next;
2900 unsigned int override = 0;
2902 actor = GetChannelUser(channel->channel_info, user->handle_info);
2903 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2905 if(min_access > max_access)
2907 reply("CSMSG_BAD_RANGE", min_access, max_access);
2911 if(actor->access <= max_access)
2913 reply("CSMSG_NO_ACCESS");
2917 if(!real_actor || real_actor->access <= max_access)
2918 override = CMD_LOG_OVERRIDE;
2920 for(uData = channel->channel_info->users; uData; uData = next)
2924 if((uData->access >= min_access)
2925 && (uData->access <= max_access)
2926 && match_ircglob(uData->handle->handle, mask))
2927 del_channel_user(uData, 1);
2930 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2931 return 1 | override;
2934 static CHANSERV_FUNC(cmd_mdelowner)
2936 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2939 static CHANSERV_FUNC(cmd_mdelcoowner)
2941 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2944 static CHANSERV_FUNC(cmd_mdelmaster)
2946 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2949 static CHANSERV_FUNC(cmd_mdelop)
2951 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2954 static CHANSERV_FUNC(cmd_mdelpeon)
2956 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2960 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2962 struct banData *bData, *next;
2963 char interval[INTERVALLEN];
2965 unsigned long limit;
2968 limit = now - duration;
2969 for(bData = channel->channel_info->bans; bData; bData = next)
2973 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2976 del_channel_ban(bData);
2980 intervalString(interval, duration, user->handle_info);
2981 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2986 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2988 struct userData *actor, *uData, *next;
2989 char interval[INTERVALLEN];
2991 unsigned long limit;
2993 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2994 if(min_access > max_access)
2996 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3000 if(!actor || actor->access <= max_access)
3002 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3007 limit = now - duration;
3008 for(uData = channel->channel_info->users; uData; uData = next)
3012 if((uData->seen > limit)
3014 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3017 if(((uData->access >= min_access) && (uData->access <= max_access))
3018 || (!max_access && (uData->access < actor->access)))
3020 del_channel_user(uData, 1);
3028 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3030 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3034 static CHANSERV_FUNC(cmd_trim)
3036 unsigned long duration;
3037 unsigned short min_level, max_level;
3042 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3043 duration = ParseInterval(argv[2]);
3046 reply("CSMSG_CANNOT_TRIM");
3050 if(!irccasecmp(argv[1], "bans"))
3052 cmd_trim_bans(user, channel, duration);
3055 else if(!irccasecmp(argv[1], "users"))
3057 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3060 else if(parse_level_range(&min_level, &max_level, argv[1]))
3062 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3065 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3067 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3072 reply("CSMSG_INVALID_TRIM", argv[1]);
3077 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3078 to the user. cmd_all takes advantage of this. */
3079 static CHANSERV_FUNC(cmd_up)
3081 struct mod_chanmode change;
3082 struct userData *uData;
3085 mod_chanmode_init(&change);
3087 change.args[0].u.member = GetUserMode(channel, user);
3088 if(!change.args[0].u.member)
3091 reply("MSG_CHANNEL_ABSENT", channel->name);
3095 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3099 reply("CSMSG_GODMODE_UP", argv[0]);
3102 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3104 change.args[0].mode = MODE_CHANOP;
3105 errmsg = "CSMSG_ALREADY_OPPED";
3107 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3109 change.args[0].mode = MODE_VOICE;
3110 errmsg = "CSMSG_ALREADY_VOICED";
3115 reply("CSMSG_NO_ACCESS");
3118 change.args[0].mode &= ~change.args[0].u.member->modes;
3119 if(!change.args[0].mode)
3122 reply(errmsg, channel->name);
3125 modcmd_chanmode_announce(&change);
3129 static CHANSERV_FUNC(cmd_down)
3131 struct mod_chanmode change;
3133 mod_chanmode_init(&change);
3135 change.args[0].u.member = GetUserMode(channel, user);
3136 if(!change.args[0].u.member)
3139 reply("MSG_CHANNEL_ABSENT", channel->name);
3143 if(!change.args[0].u.member->modes)
3146 reply("CSMSG_ALREADY_DOWN", channel->name);
3150 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3151 modcmd_chanmode_announce(&change);
3155 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)
3157 struct userData *cList;
3159 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3161 if(IsSuspended(cList->channel)
3162 || IsUserSuspended(cList)
3163 || !GetUserMode(cList->channel->channel, user))
3166 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3172 static CHANSERV_FUNC(cmd_upall)
3174 return cmd_all(CSFUNC_ARGS, cmd_up);
3177 static CHANSERV_FUNC(cmd_downall)
3179 return cmd_all(CSFUNC_ARGS, cmd_down);
3182 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3183 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3186 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)
3188 unsigned int ii, valid;
3189 struct userNode *victim;
3190 struct mod_chanmode *change;
3192 change = mod_chanmode_alloc(argc - 1);
3194 for(ii=valid=0; ++ii < argc; )
3196 if(!(victim = GetUserH(argv[ii])))
3198 change->args[valid].mode = mode;
3199 change->args[valid].u.member = GetUserMode(channel, victim);
3200 if(!change->args[valid].u.member)
3202 if(validate && !validate(user, channel, victim))
3207 change->argc = valid;
3208 if(valid < (argc-1))
3209 reply("CSMSG_PROCESS_FAILED");
3212 modcmd_chanmode_announce(change);
3213 reply(action, channel->name);
3215 mod_chanmode_free(change);
3219 static CHANSERV_FUNC(cmd_op)
3221 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3224 static CHANSERV_FUNC(cmd_deop)
3226 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3229 static CHANSERV_FUNC(cmd_voice)
3231 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3234 static CHANSERV_FUNC(cmd_devoice)
3236 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3239 static CHANSERV_FUNC(cmd_opme)
3241 struct mod_chanmode change;
3244 mod_chanmode_init(&change);
3246 change.args[0].u.member = GetUserMode(channel, user);
3247 if(!change.args[0].u.member)
3250 reply("MSG_CHANNEL_ABSENT", channel->name);
3254 struct devnull_class *devnull;
3255 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3257 change.args[0].mode = MODE_CHANOP;
3258 errmsg = "CSMSG_ALREADY_OPPED";
3263 reply("CSMSG_NO_ACCESS");
3266 change.args[0].mode &= ~change.args[0].u.member->modes;
3267 if(!change.args[0].mode)
3270 reply(errmsg, channel->name);
3273 modcmd_chanmode_announce(&change);
3278 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3284 for(ii=0; ii<channel->members.used; ii++)
3286 struct modeNode *mn = channel->members.list[ii];
3288 if(IsService(mn->user))
3291 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3294 if(protect_user(mn->user, user, channel->channel_info))
3298 victims[(*victimCount)++] = mn;
3304 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3306 struct userNode *victim;
3307 struct modeNode **victims;
3308 unsigned int offset, n, victimCount, duration = 0;
3309 char *reason = "Bye.", *ban, *name;
3310 char interval[INTERVALLEN];
3312 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3313 REQUIRE_PARAMS(offset);
3314 if(argc > offset && IsNetServ(user))
3316 if(*argv[offset] == '$') {
3317 struct userNode *hib;
3318 const char *accountnameb = argv[offset] + 1;
3319 if(!(hib = GetUserH(accountnameb)))
3321 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3330 reason = unsplit_string(argv + offset, argc - offset, NULL);
3331 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3333 /* Truncate the reason to a length of TOPICLEN, as
3334 the ircd does; however, leave room for an ellipsis
3335 and the kicker's nick. */
3336 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3340 if((victim = GetUserH(argv[1])))
3342 victims = alloca(sizeof(victims[0]));
3343 victims[0] = GetUserMode(channel, victim);
3344 /* XXX: The comparison with ACTION_KICK is just because all
3345 * other actions can work on users outside the channel, and we
3346 * want to allow those (e.g. unbans) in that case. If we add
3347 * some other ejection action for in-channel users, change
3349 victimCount = victims[0] ? 1 : 0;
3351 if(IsService(victim))
3353 reply("MSG_SERVICE_IMMUNE", victim->nick);
3357 if((action == ACTION_KICK) && !victimCount)
3359 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3363 if(protect_user(victim, user, channel->channel_info))
3365 reply("CSMSG_USER_PROTECTED", victim->nick);
3369 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3370 name = victim->nick;
3372 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3374 struct handle_info *hi;
3375 extern const char *titlehost_suffix;
3376 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3377 const char *accountname = argv[1] + 1;
3379 if(!(hi = get_handle_info(accountname)))
3381 reply("MSG_HANDLE_UNKNOWN", accountname);
3385 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3386 victims = alloca(sizeof(victims[0]) * channel->members.used);
3388 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3390 reply("CSMSG_MASK_PROTECTED", banmask);
3394 if((action == ACTION_KICK) && (victimCount == 0))
3396 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3400 name = ban = strdup(banmask);
3404 if(!is_ircmask(argv[1]))
3406 reply("MSG_NICK_UNKNOWN", argv[1]);
3410 victims = alloca(sizeof(victims[0]) * channel->members.used);
3412 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3414 reply("CSMSG_MASK_PROTECTED", argv[1]);
3418 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3420 reply("CSMSG_LAME_MASK", argv[1]);
3424 if((action == ACTION_KICK) && (victimCount == 0))
3426 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3430 name = ban = strdup(argv[1]);
3433 /* Truncate the ban in place if necessary; we must ensure
3434 that 'ban' is a valid ban mask before sanitizing it. */
3435 sanitize_ircmask(ban);
3437 if(action & ACTION_ADD_BAN)
3439 struct banData *bData, *next;
3441 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3443 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3448 if(action & ACTION_ADD_TIMED_BAN)
3450 duration = ParseInterval(argv[2]);
3454 reply("CSMSG_DURATION_TOO_LOW");
3458 else if(duration > (86400 * 365 * 2))
3460 reply("CSMSG_DURATION_TOO_HIGH");
3466 for(bData = channel->channel_info->bans; bData; bData = next)
3468 if(match_ircglobs(bData->mask, ban))
3470 int exact = !irccasecmp(bData->mask, ban);
3472 /* The ban is redundant; there is already a ban
3473 with the same effect in place. */
3477 free(bData->reason);
3478 bData->reason = strdup(reason);
3479 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3481 reply("CSMSG_REASON_CHANGE", ban);
3485 if(exact && bData->expires)
3489 /* If the ban matches an existing one exactly,
3490 extend the expiration time if the provided
3491 duration is longer. */
3492 if(duration && (now + duration > bData->expires))
3494 bData->expires = now + duration;
3505 /* Delete the expiration timeq entry and
3506 requeue if necessary. */
3507 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3510 timeq_add(bData->expires, expire_ban, bData);
3514 /* automated kickban */
3517 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3519 reply("CSMSG_BAN_ADDED", name, channel->name);
3525 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3532 if(match_ircglobs(ban, bData->mask))
3534 /* The ban we are adding makes previously existing
3535 bans redundant; silently remove them. */
3536 del_channel_ban(bData);
3540 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);
3542 name = ban = strdup(bData->mask);
3546 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3548 extern const char *hidden_host_suffix;
3549 const char *old_name = chanserv_conf.old_ban_names->list[n];
3551 unsigned int l1, l2;
3554 l2 = strlen(old_name);
3557 if(irccasecmp(ban + l1 - l2, old_name))
3559 new_mask = malloc(MAXLEN);
3560 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3562 name = ban = new_mask;
3567 if(action & ACTION_BAN)
3569 unsigned int exists;
3570 struct mod_chanmode *change;
3572 if(channel->banlist.used >= MAXBANS)
3575 reply("CSMSG_BANLIST_FULL", channel->name);
3580 exists = ChannelBanExists(channel, ban);
3581 change = mod_chanmode_alloc(victimCount + 1);
3582 for(n = 0; n < victimCount; ++n)
3584 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3585 change->args[n].u.member = victims[n];
3589 change->args[n].mode = MODE_BAN;
3590 change->args[n++].u.hostmask = ban;
3594 modcmd_chanmode_announce(change);
3596 mod_chanmode_announce(chanserv, channel, change);
3597 mod_chanmode_free(change);
3599 if(exists && (action == ACTION_BAN))
3602 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3608 if(action & ACTION_KICK)
3610 char kick_reason[MAXLEN];
3611 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3613 for(n = 0; n < victimCount; n++)
3614 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3619 /* No response, since it was automated. */
3621 else if(action & ACTION_ADD_BAN)
3624 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3626 reply("CSMSG_BAN_ADDED", name, channel->name);
3628 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3629 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3630 else if(action & ACTION_BAN)
3631 reply("CSMSG_BAN_DONE", name, channel->name);
3632 else if(action & ACTION_KICK && victimCount)
3633 reply("CSMSG_KICK_DONE", name, channel->name);
3639 static CHANSERV_FUNC(cmd_kickban)
3641 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3644 static CHANSERV_FUNC(cmd_kick)
3646 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3649 static CHANSERV_FUNC(cmd_ban)
3651 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3654 static CHANSERV_FUNC(cmd_addban)
3656 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3659 static CHANSERV_FUNC(cmd_addtimedban)
3661 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3664 struct mod_chanmode *
3665 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3667 struct mod_chanmode *change;
3668 unsigned char *match;
3669 unsigned int ii, count;
3671 match = alloca(bans->used);
3674 for(ii = count = 0; ii < bans->used; ++ii)
3676 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3677 MATCH_USENICK | MATCH_VISIBLE);
3684 for(ii = count = 0; ii < bans->used; ++ii)
3686 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3693 change = mod_chanmode_alloc(count);
3694 for(ii = count = 0; ii < bans->used; ++ii)
3698 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3699 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3701 assert(count == change->argc);
3706 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3708 struct userNode *actee;
3714 /* may want to allow a comma delimited list of users... */
3715 if(!(actee = GetUserH(argv[1])))
3717 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3719 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3720 const char *accountname = argv[1] + 1;
3722 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3723 mask = strdup(banmask);
3725 else if(!is_ircmask(argv[1]))
3727 reply("MSG_NICK_UNKNOWN", argv[1]);
3732 mask = strdup(argv[1]);
3736 /* We don't sanitize the mask here because ircu
3738 if(action & ACTION_UNBAN)
3740 struct mod_chanmode *change;
3741 change = find_matching_bans(&channel->banlist, actee, mask);
3746 modcmd_chanmode_announce(change);
3747 for(ii = 0; ii < change->argc; ++ii)
3748 free((char*)change->args[ii].u.hostmask);
3749 mod_chanmode_free(change);
3754 if(action & ACTION_DEL_BAN)
3756 struct banData *ban, *next;
3758 ban = channel->channel_info->bans;
3762 for( ; ban && !user_matches_glob(actee, ban->mask,
3763 MATCH_USENICK | MATCH_VISIBLE);
3766 for( ; ban && !match_ircglobs(mask, ban->mask);
3771 del_channel_ban(ban);
3778 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3780 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3786 static CHANSERV_FUNC(cmd_unban)
3788 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3791 static CHANSERV_FUNC(cmd_delban)
3793 /* it doesn't necessarily have to remove the channel ban - may want
3794 to make that an option. */
3795 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3798 static CHANSERV_FUNC(cmd_unbanme)
3800 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3801 long flags = ACTION_UNBAN;
3803 /* remove permanent bans if the user has the proper access. */
3804 if(uData->access >= UL_MASTER)
3805 flags |= ACTION_DEL_BAN;
3807 argv[1] = user->nick;
3808 return unban_user(user, channel, 2, argv, cmd, flags);
3811 static CHANSERV_FUNC(cmd_unbanall)
3813 struct mod_chanmode *change;
3816 if(!channel->banlist.used)
3818 reply("CSMSG_NO_BANS", channel->name);
3822 change = mod_chanmode_alloc(channel->banlist.used);
3823 for(ii=0; ii<channel->banlist.used; ii++)
3825 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3826 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3828 modcmd_chanmode_announce(change);
3829 for(ii = 0; ii < change->argc; ++ii)
3830 free((char*)change->args[ii].u.hostmask);
3831 mod_chanmode_free(change);
3832 reply("CSMSG_BANS_REMOVED", channel->name);
3836 static CHANSERV_FUNC(cmd_open)
3838 struct mod_chanmode *change;
3841 change = find_matching_bans(&channel->banlist, user, NULL);
3843 change = mod_chanmode_alloc(0);
3844 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3845 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3846 && channel->channel_info->modes.modes_set)
3847 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3848 modcmd_chanmode_announce(change);
3849 reply("CSMSG_CHANNEL_OPENED", channel->name);
3850 for(ii = 0; ii < change->argc; ++ii)
3851 free((char*)change->args[ii].u.hostmask);
3852 mod_chanmode_free(change);
3856 static CHANSERV_FUNC(cmd_myaccess)
3858 static struct string_buffer sbuf;
3859 struct handle_info *target_handle;
3860 struct userData *uData;
3865 target_handle = user->handle_info;
3866 else if(!IsStaff(user))
3868 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3871 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3874 if(!oper_outranks(user, target_handle))
3877 if(!target_handle->channels)
3879 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3883 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3884 for(uData = target_handle->channels; uData; uData = uData->u_next)
3886 struct chanData *cData = uData->channel;
3889 if(uData->access > UL_OWNER)
3891 if(uData->access == UL_OWNER)
3894 if(IsProtected(cData)
3895 && (target_handle != user->handle_info)
3896 && !GetTrueChannelAccess(cData, user->handle_info)
3897 && !IsNetworkHelper(user))
3900 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3901 if(uData->flags != USER_AUTO_OP)
3902 string_buffer_append(&sbuf, ',');
3903 if(IsUserSuspended(uData))
3904 string_buffer_append(&sbuf, 's');
3905 if(IsUserAutoOp(uData))
3907 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3908 string_buffer_append(&sbuf, 'o');
3909 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3910 string_buffer_append(&sbuf, 'v');
3912 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3913 string_buffer_append(&sbuf, 'i');
3915 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3917 string_buffer_append_string(&sbuf, ")]");
3918 string_buffer_append(&sbuf, '\0');
3919 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3923 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3925 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3931 static CHANSERV_FUNC(cmd_access)
3933 struct userNode *target;
3934 struct handle_info *target_handle;
3935 struct userData *uData;
3937 char prefix[MAXLEN];
3942 target_handle = target->handle_info;
3944 else if((target = GetUserH(argv[1])))
3946 target_handle = target->handle_info;
3948 else if(argv[1][0] == '*')
3950 if(!(target_handle = get_handle_info(argv[1]+1)))
3952 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3958 reply("MSG_NICK_UNKNOWN", argv[1]);
3962 assert(target || target_handle);
3964 if(target == chanserv)
3966 reply("CSMSG_IS_CHANSERV");
3974 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3979 reply("MSG_USER_AUTHENTICATE", target->nick);
3982 reply("MSG_AUTHENTICATE");
3988 const char *epithet = NULL, *type = NULL;
3991 epithet = chanserv_conf.irc_operator_epithet;
3992 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3994 else if(IsNetworkHelper(target))
3996 epithet = chanserv_conf.network_helper_epithet;
3997 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3999 else if(IsSupportHelper(target))
4001 epithet = chanserv_conf.support_helper_epithet;
4002 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4006 if(target_handle->epithet)
4007 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4009 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4011 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4015 sprintf(prefix, "%s", target_handle->handle);
4018 if(!channel->channel_info)
4020 reply("CSMSG_NOT_REGISTERED", channel->name);
4024 helping = HANDLE_FLAGGED(target_handle, HELPING)
4025 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4026 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4028 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4029 /* To prevent possible information leaks, only show infolines
4030 * if the requestor is in the channel or it's their own
4032 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4034 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4036 /* Likewise, only say it's suspended if the user has active
4037 * access in that channel or it's their own entry. */
4038 if(IsUserSuspended(uData)
4039 && (GetChannelUser(channel->channel_info, user->handle_info)
4040 || (user->handle_info == uData->handle)))
4042 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4047 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4054 def_list(struct listData *list)
4058 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4060 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4061 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4062 if(list->table.length == 1)
4064 msg = user_find_message(list->user, "MSG_NONE");
4065 send_message_type(4, list->user, list->bot, " %s", msg);
4070 userData_access_comp(const void *arg_a, const void *arg_b)
4072 const struct userData *a = *(struct userData**)arg_a;
4073 const struct userData *b = *(struct userData**)arg_b;
4075 if(a->access != b->access)
4076 res = b->access - a->access;
4078 res = irccasecmp(a->handle->handle, b->handle->handle);
4083 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4085 void (*send_list)(struct listData *);
4086 struct userData *uData;
4087 struct listData lData;
4088 unsigned int matches;
4092 lData.bot = cmd->parent->bot;
4093 lData.channel = channel;
4094 lData.lowest = lowest;
4095 lData.highest = highest;
4096 lData.search = (argc > 1) ? argv[1] : NULL;
4097 send_list = def_list;
4099 if(user->handle_info)
4101 switch(user->handle_info->userlist_style)
4103 case HI_STYLE_DEF: send_list = def_list; break;
4104 case HI_STYLE_ZOOT: send_list = def_list; break;
4108 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4110 for(uData = channel->channel_info->users; uData; uData = uData->next)
4112 if((uData->access < lowest)
4113 || (uData->access > highest)
4114 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4116 lData.users[matches++] = uData;
4118 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4120 lData.table.length = matches+1;
4121 lData.table.width = 4;
4122 lData.table.flags = TABLE_NO_FREE;
4123 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4124 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4125 lData.table.contents[0] = ary;
4128 ary[2] = "Last Seen";
4130 for(matches = 1; matches < lData.table.length; ++matches)
4132 char seen[INTERVALLEN];
4134 uData = lData.users[matches-1];
4135 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4136 lData.table.contents[matches] = ary;
4137 ary[0] = strtab(uData->access);
4138 ary[1] = uData->handle->handle;
4141 else if(!uData->seen)
4144 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4145 ary[2] = strdup(ary[2]);
4146 if(IsUserSuspended(uData))
4147 ary[3] = "Suspended";
4148 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4149 ary[3] = "Vacation";
4150 else if(HANDLE_FLAGGED(uData->handle, BOT))
4156 for(matches = 1; matches < lData.table.length; ++matches)
4158 free((char*)lData.table.contents[matches][2]);
4159 free(lData.table.contents[matches]);
4161 free(lData.table.contents[0]);
4162 free(lData.table.contents);
4163 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4167 static CHANSERV_FUNC(cmd_users)
4169 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4172 static CHANSERV_FUNC(cmd_wlist)
4174 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4177 static CHANSERV_FUNC(cmd_clist)
4179 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4182 static CHANSERV_FUNC(cmd_mlist)
4184 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4187 static CHANSERV_FUNC(cmd_olist)
4189 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4192 static CHANSERV_FUNC(cmd_plist)
4194 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4197 static CHANSERV_FUNC(cmd_bans)
4199 struct userNode *search_u = NULL;
4200 struct helpfile_table tbl;
4201 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4202 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4203 const char *msg_never, *triggered, *expires;
4204 struct banData *ban, **bans;
4208 else if(strchr(search = argv[1], '!'))
4211 search_wilds = search[strcspn(search, "?*")];
4213 else if(!(search_u = GetUserH(search)))
4214 reply("MSG_NICK_UNKNOWN", search);
4216 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4218 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4222 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4227 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4230 bans[matches++] = ban;
4235 tbl.length = matches + 1;
4236 tbl.width = 4 + timed;
4238 tbl.flags = TABLE_NO_FREE;
4239 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4240 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4241 tbl.contents[0][0] = "Mask";
4242 tbl.contents[0][1] = "Set By";
4243 tbl.contents[0][2] = "Triggered";
4246 tbl.contents[0][3] = "Expires";
4247 tbl.contents[0][4] = "Reason";
4250 tbl.contents[0][3] = "Reason";
4253 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4255 free(tbl.contents[0]);
4260 msg_never = user_find_message(user, "MSG_NEVER");
4261 for(ii = 0; ii < matches; )
4267 else if(ban->expires)
4268 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4270 expires = msg_never;
4273 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4275 triggered = msg_never;
4277 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4278 tbl.contents[ii][0] = ban->mask;
4279 tbl.contents[ii][1] = ban->owner;
4280 tbl.contents[ii][2] = strdup(triggered);
4283 tbl.contents[ii][3] = strdup(expires);
4284 tbl.contents[ii][4] = ban->reason;
4287 tbl.contents[ii][3] = ban->reason;
4289 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4290 reply("MSG_MATCH_COUNT", matches);
4291 for(ii = 1; ii < tbl.length; ++ii)
4293 free((char*)tbl.contents[ii][2]);
4295 free((char*)tbl.contents[ii][3]);
4296 free(tbl.contents[ii]);
4298 free(tbl.contents[0]);
4304 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4306 struct chanData *cData = channel->channel_info;
4307 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4309 if(cData->topic_mask)
4310 return !match_ircglob(new_topic, cData->topic_mask);
4311 else if(cData->topic)
4312 return irccasecmp(new_topic, cData->topic);
4317 static CHANSERV_FUNC(cmd_topic)
4319 struct chanData *cData;
4322 cData = channel->channel_info;
4327 SetChannelTopic(channel, chanserv, cData->topic, 1);
4328 reply("CSMSG_TOPIC_SET", cData->topic);
4332 reply("CSMSG_NO_TOPIC", channel->name);
4336 topic = unsplit_string(argv + 1, argc - 1, NULL);
4337 /* If they say "!topic *", use an empty topic. */
4338 if((topic[0] == '*') && (topic[1] == 0))
4340 if(bad_topic(channel, user, topic))
4342 char *topic_mask = cData->topic_mask;
4345 char new_topic[TOPICLEN+1], tchar;
4346 int pos=0, starpos=-1, dpos=0, len;
4348 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4355 len = strlen(topic);
4356 if((dpos + len) > TOPICLEN)
4357 len = TOPICLEN + 1 - dpos;
4358 memcpy(new_topic+dpos, topic, len);
4362 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4363 default: new_topic[dpos++] = tchar; break;
4366 if((dpos > TOPICLEN) || tchar)
4369 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4370 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4373 new_topic[dpos] = 0;
4374 SetChannelTopic(channel, chanserv, new_topic, 1);
4376 reply("CSMSG_TOPIC_LOCKED", channel->name);
4381 SetChannelTopic(channel, chanserv, topic, 1);
4383 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4385 /* Grab the topic and save it as the default topic. */
4387 cData->topic = strdup(channel->topic);
4393 static CHANSERV_FUNC(cmd_mode)
4395 struct userData *uData;
4396 struct mod_chanmode *change;
4402 change = &channel->channel_info->modes;
4403 if(change->modes_set || change->modes_clear) {
4404 modcmd_chanmode_announce(change);
4405 reply("CSMSG_DEFAULTED_MODES", channel->name);
4407 reply("CSMSG_NO_MODES", channel->name);
4411 uData = GetChannelUser(channel->channel_info, user->handle_info);
4413 base_oplevel = MAXOPLEVEL;
4414 else if (uData->access >= UL_OWNER)
4417 base_oplevel = 1 + UL_OWNER - uData->access;
4418 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4421 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4425 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4426 && mode_lock_violated(&channel->channel_info->modes, change))
4429 mod_chanmode_format(&channel->channel_info->modes, modes);
4430 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4434 modcmd_chanmode_announce(change);
4435 mod_chanmode_format(change, fmt);
4436 mod_chanmode_free(change);
4437 reply("CSMSG_MODES_SET", fmt);
4442 chanserv_del_invite_mark(void *data)
4444 struct ChanUser *chanuser = data;
4445 struct chanNode *channel = chanuser->chan;
4447 if(!channel) return;
4448 for(i = 0; i < channel->invited.used; i++)
4450 if(channel->invited.list[i] == chanuser->user) {
4451 userList_remove(&channel->invited, chanuser->user);
4457 static CHANSERV_FUNC(cmd_invite)
4459 struct userNode *invite;
4460 struct ChanUser *chanuser;
4465 if(!(invite = GetUserH(argv[1])))
4467 reply("MSG_NICK_UNKNOWN", argv[1]);
4474 if(GetUserMode(channel, invite))
4476 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4480 for(i = 0; i < channel->invited.used; i++)
4482 if(channel->invited.list[i] == invite) {
4483 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4492 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4493 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4496 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4498 irc_invite(chanserv, invite, channel);
4500 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4502 userList_append(&channel->invited, invite);
4503 chanuser = calloc(1, sizeof(*chanuser));
4504 chanuser->user=invite;
4505 chanuser->chan=channel;
4506 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4511 static CHANSERV_FUNC(cmd_inviteme)
4513 if(GetUserMode(channel, user))
4515 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4518 if(channel->channel_info
4519 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4521 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4524 irc_invite(cmd->parent->bot, user, channel);
4528 static CHANSERV_FUNC(cmd_invitemeall)
4530 struct handle_info *target = user->handle_info;
4531 struct userData *uData;
4533 if(!target->channels)
4535 reply("CSMSG_SQUAT_ACCESS", target->handle);
4539 for(uData = target->channels; uData; uData = uData->u_next)
4541 struct chanData *cData = uData->channel;
4542 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4544 irc_invite(cmd->parent->bot, user, cData->channel);
4551 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4554 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4556 /* We display things based on two dimensions:
4557 * - Issue time: present or absent
4558 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4559 * (in order of precedence, so something both expired and revoked
4560 * only counts as revoked)
4562 combo = (suspended->issued ? 4 : 0)
4563 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4565 case 0: /* no issue time, indefinite expiration */
4566 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4568 case 1: /* no issue time, expires in future */
4569 intervalString(buf1, suspended->expires-now, user->handle_info);
4570 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4572 case 2: /* no issue time, expired */
4573 intervalString(buf1, now-suspended->expires, user->handle_info);
4574 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4576 case 3: /* no issue time, revoked */
4577 intervalString(buf1, now-suspended->revoked, user->handle_info);
4578 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4580 case 4: /* issue time set, indefinite expiration */
4581 intervalString(buf1, now-suspended->issued, user->handle_info);
4582 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4584 case 5: /* issue time set, expires in future */
4585 intervalString(buf1, now-suspended->issued, user->handle_info);
4586 intervalString(buf2, suspended->expires-now, user->handle_info);
4587 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4589 case 6: /* issue time set, expired */
4590 intervalString(buf1, now-suspended->issued, user->handle_info);
4591 intervalString(buf2, now-suspended->expires, user->handle_info);
4592 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4594 case 7: /* issue time set, revoked */
4595 intervalString(buf1, now-suspended->issued, user->handle_info);
4596 intervalString(buf2, now-suspended->revoked, user->handle_info);
4597 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4600 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4605 static CHANSERV_FUNC(cmd_info)
4607 char modes[MAXLEN], buffer[INTERVALLEN];
4608 struct userData *uData, *owner;
4609 struct chanData *cData;
4610 struct do_not_register *dnr;
4615 cData = channel->channel_info;
4616 reply("CSMSG_CHANNEL_INFO", channel->name);
4618 uData = GetChannelUser(cData, user->handle_info);
4619 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4621 mod_chanmode_format(&cData->modes, modes);
4622 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4623 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4626 for(it = dict_first(cData->notes); it; it = iter_next(it))
4630 note = iter_data(it);
4631 if(!note_type_visible_to_user(cData, note->type, user))
4634 padding = PADLEN - 1 - strlen(iter_key(it));
4635 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4638 if(cData->max_time) {
4639 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4641 reply("CSMSG_CHANNEL_MAX", cData->max);
4643 for(owner = cData->users; owner; owner = owner->next)
4644 if(owner->access == UL_OWNER)
4645 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4646 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4647 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4648 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4650 privileged = IsStaff(user);
4652 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4653 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4654 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4656 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4657 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4659 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4661 struct suspended *suspended;
4662 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4663 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4664 show_suspension_info(cmd, user, suspended);
4666 else if(IsSuspended(cData))
4668 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4669 show_suspension_info(cmd, user, cData->suspended);
4674 static CHANSERV_FUNC(cmd_netinfo)
4676 extern unsigned long boot_time;
4677 extern unsigned long burst_length;
4678 char interval[INTERVALLEN];
4680 reply("CSMSG_NETWORK_INFO");
4681 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4682 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4683 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4684 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4685 reply("CSMSG_NETWORK_BANS", banCount);
4686 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4687 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4688 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4693 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4695 struct helpfile_table table;
4697 struct userNode *user;
4702 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4703 table.contents = alloca(list->used*sizeof(*table.contents));
4704 for(nn=0; nn<list->used; nn++)
4706 user = list->list[nn];
4707 if(user->modes & skip_flags)
4713 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4716 nick = alloca(strlen(user->nick)+3);
4717 sprintf(nick, "(%s)", user->nick);
4721 table.contents[table.length][0] = nick;
4724 table_send(chanserv, to->nick, 0, NULL, table);
4727 static CHANSERV_FUNC(cmd_ircops)
4729 reply("CSMSG_STAFF_OPERS");
4730 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4734 static CHANSERV_FUNC(cmd_helpers)
4736 reply("CSMSG_STAFF_HELPERS");
4737 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4741 static CHANSERV_FUNC(cmd_staff)
4743 reply("CSMSG_NETWORK_STAFF");
4744 cmd_ircops(CSFUNC_ARGS);
4745 cmd_helpers(CSFUNC_ARGS);
4749 static CHANSERV_FUNC(cmd_peek)
4751 struct modeNode *mn;
4752 char modes[MODELEN];
4754 struct helpfile_table table;
4755 int opcount = 0, voicecount = 0, srvcount = 0;
4757 irc_make_chanmode(channel, modes);
4759 reply("CSMSG_PEEK_INFO", channel->name);
4760 reply("CSMSG_PEEK_TOPIC", channel->topic);
4761 reply("CSMSG_PEEK_MODES", modes);
4765 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4766 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4767 for(n = 0; n < channel->members.used; n++)
4769 mn = channel->members.list[n];
4770 if(IsLocal(mn->user))
4772 else if(mn->modes & MODE_CHANOP)
4774 else if(mn->modes & MODE_VOICE)
4777 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4779 table.contents[table.length] = alloca(sizeof(**table.contents));
4780 table.contents[table.length][0] = mn->user->nick;
4784 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4785 (channel->members.used - opcount - voicecount - srvcount));
4789 reply("CSMSG_PEEK_OPS");
4790 table_send(chanserv, user->nick, 0, NULL, table);
4793 reply("CSMSG_PEEK_NO_OPS");
4797 static MODCMD_FUNC(cmd_wipeinfo)
4799 struct handle_info *victim;
4800 struct userData *ud, *actor, *real_actor;
4801 unsigned int override = 0;
4804 actor = GetChannelUser(channel->channel_info, user->handle_info);
4805 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4806 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4808 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4810 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4813 if((ud->access >= actor->access) && (ud != actor))
4815 reply("MSG_USER_OUTRANKED", victim->handle);
4818 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4819 override = CMD_LOG_OVERRIDE;
4823 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4824 return 1 | override;
4827 static CHANSERV_FUNC(cmd_resync)
4829 struct mod_chanmode *changes;
4830 struct chanData *cData = channel->channel_info;
4831 unsigned int ii, used;
4833 changes = mod_chanmode_alloc(channel->members.used * 2);
4834 for(ii = used = 0; ii < channel->members.used; ++ii)
4836 struct modeNode *mn = channel->members.list[ii];
4837 struct userData *uData;
4839 if(IsService(mn->user))
4842 uData = GetChannelAccess(cData, mn->user->handle_info);
4843 if(!cData->lvlOpts[lvlGiveOps]
4844 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4846 if(!(mn->modes & MODE_CHANOP))
4848 if(!uData || IsUserAutoOp(uData))
4850 changes->args[used].mode = MODE_CHANOP;
4851 changes->args[used++].u.member = mn;
4852 if(!(mn->modes & MODE_VOICE))
4854 changes->args[used].mode = MODE_VOICE;
4855 changes->args[used++].u.member = mn;
4860 else if(!cData->lvlOpts[lvlGiveVoice]
4861 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4863 if(mn->modes & MODE_CHANOP)
4865 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4866 changes->args[used++].u.member = mn;
4868 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4870 changes->args[used].mode = MODE_VOICE;
4871 changes->args[used++].u.member = mn;
4878 changes->args[used].mode = MODE_REMOVE | mn->modes;
4879 changes->args[used++].u.member = mn;
4883 changes->argc = used;
4884 modcmd_chanmode_announce(changes);
4885 mod_chanmode_free(changes);
4886 reply("CSMSG_RESYNCED_USERS", channel->name);
4890 static CHANSERV_FUNC(cmd_seen)
4892 struct userData *uData;
4893 struct handle_info *handle;
4894 char seen[INTERVALLEN];
4898 if(!irccasecmp(argv[1], chanserv->nick))
4900 reply("CSMSG_IS_CHANSERV");
4904 if(!(handle = get_handle_info(argv[1])))
4906 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4910 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4912 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4917 reply("CSMSG_USER_PRESENT", handle->handle);
4918 else if(uData->seen)
4919 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4921 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4923 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4924 reply("CSMSG_USER_VACATION", handle->handle);
4929 static MODCMD_FUNC(cmd_names)
4931 struct userNode *targ;
4932 struct userData *targData;
4933 unsigned int ii, pos;
4936 for(ii=pos=0; ii<channel->members.used; ++ii)
4938 targ = channel->members.list[ii]->user;
4939 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4942 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4945 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4949 if(IsUserSuspended(targData))
4951 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4954 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4955 reply("CSMSG_END_NAMES", channel->name);
4960 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4962 switch(ntype->visible_type)
4964 case NOTE_VIS_ALL: return 1;
4965 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4966 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4971 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4973 struct userData *uData;
4975 switch(ntype->set_access_type)
4977 case NOTE_SET_CHANNEL_ACCESS:
4978 if(!user->handle_info)
4980 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4982 return uData->access >= ntype->set_access.min_ulevel;
4983 case NOTE_SET_CHANNEL_SETTER:
4984 return check_user_level(channel, user, lvlSetters, 1, 0);
4985 case NOTE_SET_PRIVILEGED: default:
4986 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4990 static CHANSERV_FUNC(cmd_note)
4992 struct chanData *cData;
4994 struct note_type *ntype;
4996 cData = channel->channel_info;
4999 reply("CSMSG_NOT_REGISTERED", channel->name);
5003 /* If no arguments, show all visible notes for the channel. */
5009 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5011 note = iter_data(it);
5012 if(!note_type_visible_to_user(cData, note->type, user))
5015 reply("CSMSG_NOTELIST_HEADER", channel->name);
5016 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5019 reply("CSMSG_NOTELIST_END", channel->name);
5021 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5023 /* If one argument, show the named note. */
5026 if((note = dict_find(cData->notes, argv[1], NULL))
5027 && note_type_visible_to_user(cData, note->type, user))
5029 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5031 else if((ntype = dict_find(note_types, argv[1], NULL))
5032 && note_type_visible_to_user(NULL, ntype, user))
5034 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5039 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5043 /* Assume they're trying to set a note. */
5047 ntype = dict_find(note_types, argv[1], NULL);
5050 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5053 else if(note_type_settable_by_user(channel, ntype, user))
5055 note_text = unsplit_string(argv+2, argc-2, NULL);
5056 if((note = dict_find(cData->notes, argv[1], NULL)))
5057 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5058 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5059 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5061 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5063 /* The note is viewable to staff only, so return 0
5064 to keep the invocation from getting logged (or
5065 regular users can see it in !events). */
5071 reply("CSMSG_NO_ACCESS");
5078 static CHANSERV_FUNC(cmd_delnote)
5083 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5084 || !note_type_settable_by_user(channel, note->type, user))
5086 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5089 dict_remove(channel->channel_info->notes, note->type->name);
5090 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5094 static CHANSERV_FUNC(cmd_events)
5096 struct logSearch discrim;
5097 struct logReport report;
5098 unsigned int matches, limit;
5100 limit = (argc > 1) ? atoi(argv[1]) : 10;
5101 if(limit < 1 || limit > 200)
5104 memset(&discrim, 0, sizeof(discrim));
5105 discrim.masks.bot = chanserv;
5106 discrim.masks.channel_name = channel->name;
5108 discrim.masks.command = argv[2];
5109 discrim.limit = limit;
5110 discrim.max_time = INT_MAX;
5111 discrim.severities = 1 << LOG_COMMAND;
5112 report.reporter = chanserv;
5114 reply("CSMSG_EVENT_SEARCH_RESULTS");
5115 matches = log_entry_search(&discrim, log_report_entry, &report);
5117 reply("MSG_MATCH_COUNT", matches);
5119 reply("MSG_NO_MATCHES");
5123 static CHANSERV_FUNC(cmd_say)
5129 msg = unsplit_string(argv + 1, argc - 1, NULL);
5130 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5132 else if(*argv[1] == '*' && argv[1][1] != '\0')
5134 struct handle_info *hi;
5135 struct userNode *authed;
5138 msg = unsplit_string(argv + 2, argc - 2, NULL);
5140 if (!(hi = get_handle_info(argv[1] + 1)))
5142 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5146 for (authed = hi->users; authed; authed = authed->next_authed)
5147 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5149 else if(GetUserH(argv[1]))
5152 msg = unsplit_string(argv + 2, argc - 2, NULL);
5153 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5157 reply("MSG_NOT_TARGET_NAME");
5163 static CHANSERV_FUNC(cmd_emote)
5169 /* CTCP is so annoying. */
5170 msg = unsplit_string(argv + 1, argc - 1, NULL);
5171 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5173 else if(*argv[1] == '*' && argv[1][1] != '\0')
5175 struct handle_info *hi;
5176 struct userNode *authed;
5179 msg = unsplit_string(argv + 2, argc - 2, NULL);
5181 if (!(hi = get_handle_info(argv[1] + 1)))
5183 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5187 for (authed = hi->users; authed; authed = authed->next_authed)
5188 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5190 else if(GetUserH(argv[1]))
5192 msg = unsplit_string(argv + 2, argc - 2, NULL);
5193 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5197 reply("MSG_NOT_TARGET_NAME");
5203 struct channelList *
5204 chanserv_support_channels(void)
5206 return &chanserv_conf.support_channels;
5209 static CHANSERV_FUNC(cmd_expire)
5211 int channel_count = registered_channels;
5212 expire_channels(NULL);
5213 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5218 chanserv_expire_suspension(void *data)
5220 struct suspended *suspended = data;
5221 struct chanNode *channel;
5224 /* Update the channel registration data structure. */
5225 if(!suspended->expires || (now < suspended->expires))
5226 suspended->revoked = now;
5227 channel = suspended->cData->channel;
5228 suspended->cData->channel = channel;
5229 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5231 /* If appropriate, re-join ChanServ to the channel. */
5232 if(!IsOffChannel(suspended->cData))
5234 spamserv_cs_suspend(channel, 0, 0, NULL);
5235 ss_cs_join_channel(channel, 1);
5238 /* Mark everyone currently in the channel as present. */
5239 for(ii = 0; ii < channel->members.used; ++ii)
5241 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5250 static CHANSERV_FUNC(cmd_csuspend)
5252 struct suspended *suspended;
5253 char reason[MAXLEN];
5254 unsigned long expiry, duration;
5255 struct userData *uData;
5259 if(IsProtected(channel->channel_info))
5261 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5265 if(argv[1][0] == '!')
5267 else if(IsSuspended(channel->channel_info))
5269 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5270 show_suspension_info(cmd, user, channel->channel_info->suspended);
5274 if(!strcmp(argv[1], "0"))
5276 else if((duration = ParseInterval(argv[1])))
5277 expiry = now + duration;
5280 reply("MSG_INVALID_DURATION", argv[1]);
5284 unsplit_string(argv + 2, argc - 2, reason);
5286 suspended = calloc(1, sizeof(*suspended));
5287 suspended->revoked = 0;
5288 suspended->issued = now;
5289 suspended->suspender = strdup(user->handle_info->handle);
5290 suspended->expires = expiry;
5291 suspended->reason = strdup(reason);
5292 suspended->cData = channel->channel_info;
5293 suspended->previous = suspended->cData->suspended;
5294 suspended->cData->suspended = suspended;
5296 if(suspended->expires)
5297 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5299 if(IsSuspended(channel->channel_info))
5301 suspended->previous->revoked = now;
5302 if(suspended->previous->expires)
5303 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5304 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5305 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5309 /* Mark all users in channel as absent. */
5310 for(uData = channel->channel_info->users; uData; uData = uData->next)
5319 /* Mark the channel as suspended, then part. */
5320 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5321 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5322 DelChannelUser(chanserv, channel, suspended->reason, 0);
5323 reply("CSMSG_SUSPENDED", channel->name);
5324 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5325 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5330 static CHANSERV_FUNC(cmd_cunsuspend)
5332 struct suspended *suspended;
5333 char message[MAXLEN];
5335 if(!IsSuspended(channel->channel_info))
5337 reply("CSMSG_NOT_SUSPENDED", channel->name);
5341 suspended = channel->channel_info->suspended;
5343 /* Expire the suspension and join ChanServ to the channel. */
5344 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5345 chanserv_expire_suspension(suspended);
5346 reply("CSMSG_UNSUSPENDED", channel->name);
5347 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5348 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5352 typedef struct chanservSearch
5357 unsigned long unvisited;
5358 unsigned long registered;
5360 unsigned long flags;
5364 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5367 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5372 search = malloc(sizeof(struct chanservSearch));
5373 memset(search, 0, sizeof(*search));
5376 for(i = 0; i < argc; i++)
5378 /* Assume all criteria require arguments. */
5381 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5385 if(!irccasecmp(argv[i], "name"))
5386 search->name = argv[++i];
5387 else if(!irccasecmp(argv[i], "registrar"))
5388 search->registrar = argv[++i];
5389 else if(!irccasecmp(argv[i], "unvisited"))
5390 search->unvisited = ParseInterval(argv[++i]);
5391 else if(!irccasecmp(argv[i], "registered"))
5392 search->registered = ParseInterval(argv[++i]);
5393 else if(!irccasecmp(argv[i], "flags"))
5396 if(!irccasecmp(argv[i], "nodelete"))
5397 search->flags |= CHANNEL_NODELETE;
5398 else if(!irccasecmp(argv[i], "suspended"))
5399 search->flags |= CHANNEL_SUSPENDED;
5400 else if(!irccasecmp(argv[i], "unreviewed"))
5401 search->flags |= CHANNEL_UNREVIEWED;
5404 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5408 else if(!irccasecmp(argv[i], "limit"))
5409 search->limit = strtoul(argv[++i], NULL, 10);
5412 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5417 if(search->name && !strcmp(search->name, "*"))
5419 if(search->registrar && !strcmp(search->registrar, "*"))
5420 search->registrar = 0;
5429 chanserv_channel_match(struct chanData *channel, search_t search)
5431 const char *name = channel->channel->name;
5432 if((search->name && !match_ircglob(name, search->name)) ||
5433 (search->registrar && !channel->registrar) ||
5434 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5435 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5436 (search->registered && (now - channel->registered) > search->registered) ||
5437 (search->flags && ((search->flags & channel->flags) != search->flags)))
5444 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5446 struct chanData *channel;
5447 unsigned int matches = 0;
5449 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5451 if(!chanserv_channel_match(channel, search))
5461 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5466 search_print(struct chanData *channel, void *data)
5468 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5471 static CHANSERV_FUNC(cmd_search)
5474 unsigned int matches;
5475 channel_search_func action;
5479 if(!irccasecmp(argv[1], "count"))
5480 action = search_count;
5481 else if(!irccasecmp(argv[1], "print"))
5482 action = search_print;
5485 reply("CSMSG_ACTION_INVALID", argv[1]);
5489 search = chanserv_search_create(user, argc - 2, argv + 2);
5493 if(action == search_count)
5494 search->limit = INT_MAX;
5496 if(action == search_print)
5497 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5499 matches = chanserv_channel_search(search, action, user);
5502 reply("MSG_MATCH_COUNT", matches);
5504 reply("MSG_NO_MATCHES");
5510 static CHANSERV_FUNC(cmd_unvisited)
5512 struct chanData *cData;
5513 unsigned long interval = chanserv_conf.channel_expire_delay;
5514 char buffer[INTERVALLEN];
5515 unsigned int limit = 25, matches = 0;
5519 interval = ParseInterval(argv[1]);
5521 limit = atoi(argv[2]);
5524 intervalString(buffer, interval, user->handle_info);
5525 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5527 for(cData = channelList; cData && matches < limit; cData = cData->next)
5529 if((now - cData->visited) < interval)
5532 intervalString(buffer, now - cData->visited, user->handle_info);
5533 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5540 static MODCMD_FUNC(chan_opt_defaulttopic)
5546 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5548 reply("CSMSG_TOPIC_LOCKED", channel->name);
5552 topic = unsplit_string(argv+1, argc-1, NULL);
5554 free(channel->channel_info->topic);
5555 if(topic[0] == '*' && topic[1] == 0)
5557 topic = channel->channel_info->topic = NULL;
5561 topic = channel->channel_info->topic = strdup(topic);
5562 if(channel->channel_info->topic_mask
5563 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5564 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5566 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5569 if(channel->channel_info->topic)
5570 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5572 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5576 static MODCMD_FUNC(chan_opt_topicmask)
5580 struct chanData *cData = channel->channel_info;
5583 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5585 reply("CSMSG_TOPIC_LOCKED", channel->name);
5589 mask = unsplit_string(argv+1, argc-1, NULL);
5591 if(cData->topic_mask)
5592 free(cData->topic_mask);
5593 if(mask[0] == '*' && mask[1] == 0)
5595 cData->topic_mask = 0;
5599 cData->topic_mask = strdup(mask);
5601 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5602 else if(!match_ircglob(cData->topic, cData->topic_mask))
5603 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5607 if(channel->channel_info->topic_mask)
5608 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5610 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5614 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5618 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5622 if(greeting[0] == '*' && greeting[1] == 0)
5626 unsigned int length = strlen(greeting);
5627 if(length > chanserv_conf.greeting_length)
5629 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5632 *data = strdup(greeting);
5641 reply(name, user_find_message(user, "MSG_NONE"));
5645 static MODCMD_FUNC(chan_opt_greeting)
5647 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5650 static MODCMD_FUNC(chan_opt_usergreeting)
5652 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5655 static MODCMD_FUNC(chan_opt_modes)
5657 struct mod_chanmode *new_modes;
5662 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5664 reply("CSMSG_NO_ACCESS");
5667 if(argv[1][0] == '*' && argv[1][1] == 0)
5669 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5671 else if(!(new_modes = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS|(IsOper(user) && IsHelping(user) ? MCP_OPERMODE : 0), 0)))
5673 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5676 else if(new_modes->argc > 1)
5678 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5679 mod_chanmode_free(new_modes);
5684 channel->channel_info->modes = *new_modes;
5685 modcmd_chanmode_announce(new_modes);
5686 mod_chanmode_free(new_modes);
5690 mod_chanmode_format(&channel->channel_info->modes, modes);
5692 reply("CSMSG_SET_MODES", modes);
5694 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5698 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5700 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5702 struct chanData *cData = channel->channel_info;
5707 /* Set flag according to value. */
5708 if(enabled_string(argv[1]))
5710 cData->flags |= mask;
5713 else if(disabled_string(argv[1]))
5715 cData->flags &= ~mask;
5720 reply("MSG_INVALID_BINARY", argv[1]);
5726 /* Find current option value. */
5727 value = (cData->flags & mask) ? 1 : 0;
5731 reply(name, user_find_message(user, "MSG_ON"));
5733 reply(name, user_find_message(user, "MSG_OFF"));
5737 static MODCMD_FUNC(chan_opt_nodelete)
5739 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5741 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5745 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5748 static MODCMD_FUNC(chan_opt_dynlimit)
5750 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5753 static MODCMD_FUNC(chan_opt_offchannel)
5755 struct chanData *cData = channel->channel_info;
5760 /* Set flag according to value. */
5761 if(enabled_string(argv[1]))
5763 if(!IsOffChannel(cData))
5764 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5765 cData->flags |= CHANNEL_OFFCHANNEL;
5768 else if(disabled_string(argv[1]))
5770 if(IsOffChannel(cData))
5772 struct mod_chanmode change;
5773 mod_chanmode_init(&change);
5775 change.args[0].mode = MODE_CHANOP;
5776 change.args[0].u.member = AddChannelUser(chanserv, channel);
5777 mod_chanmode_announce(chanserv, channel, &change);
5779 cData->flags &= ~CHANNEL_OFFCHANNEL;
5784 reply("MSG_INVALID_BINARY", argv[1]);
5790 /* Find current option value. */
5791 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5795 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5797 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5801 static MODCMD_FUNC(chan_opt_unreviewed)
5803 struct chanData *cData = channel->channel_info;
5804 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5810 /* The two directions can have different ACLs. */
5811 if(enabled_string(argv[1]))
5813 else if(disabled_string(argv[1]))
5817 reply("MSG_INVALID_BINARY", argv[1]);
5821 if (new_value != value)
5823 struct svccmd *subcmd;
5824 char subcmd_name[32];
5826 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5827 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5830 reply("MSG_COMMAND_DISABLED", subcmd_name);
5833 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5837 cData->flags |= CHANNEL_UNREVIEWED;
5840 free(cData->registrar);
5841 cData->registrar = strdup(user->handle_info->handle);
5842 cData->flags &= ~CHANNEL_UNREVIEWED;
5849 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5851 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5855 static MODCMD_FUNC(chan_opt_defaults)
5857 struct userData *uData;
5858 struct chanData *cData;
5859 const char *confirm;
5860 enum levelOption lvlOpt;
5861 enum charOption chOpt;
5863 cData = channel->channel_info;
5864 uData = GetChannelUser(cData, user->handle_info);
5865 if(!uData || (uData->access < UL_OWNER))
5867 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5870 confirm = make_confirmation_string(uData);
5871 if((argc < 2) || strcmp(argv[1], confirm))
5873 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5876 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5877 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5878 cData->modes = chanserv_conf.default_modes;
5879 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5880 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5881 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5882 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5883 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5888 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5890 struct chanData *cData = channel->channel_info;
5891 struct userData *uData;
5892 unsigned short value;
5896 if(!check_user_level(channel, user, option, 1, 1))
5898 reply("CSMSG_CANNOT_SET");
5901 value = user_level_from_name(argv[1], UL_OWNER+1);
5902 if(!value && strcmp(argv[1], "0"))
5904 reply("CSMSG_INVALID_ACCESS", argv[1]);
5907 uData = GetChannelUser(cData, user->handle_info);
5908 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5910 reply("CSMSG_BAD_SETLEVEL");
5916 if(value > cData->lvlOpts[lvlGiveOps])
5918 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5923 if(value < cData->lvlOpts[lvlGiveVoice])
5925 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5930 /* This test only applies to owners, since non-owners
5931 * trying to set an option to above their level get caught
5932 * by the CSMSG_BAD_SETLEVEL test above.
5934 if(value > uData->access)
5936 reply("CSMSG_BAD_SETTERS");
5943 cData->lvlOpts[option] = value;
5945 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5949 static MODCMD_FUNC(chan_opt_enfops)
5951 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5954 static MODCMD_FUNC(chan_opt_giveops)
5956 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5959 static MODCMD_FUNC(chan_opt_enfmodes)
5961 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5964 static MODCMD_FUNC(chan_opt_enftopic)
5966 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5969 static MODCMD_FUNC(chan_opt_pubcmd)
5971 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5974 static MODCMD_FUNC(chan_opt_setters)
5976 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5979 static MODCMD_FUNC(chan_opt_ctcpusers)
5981 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5984 static MODCMD_FUNC(chan_opt_userinfo)
5986 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5989 static MODCMD_FUNC(chan_opt_givevoice)
5991 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5994 static MODCMD_FUNC(chan_opt_topicsnarf)
5996 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5999 static MODCMD_FUNC(chan_opt_vote)
6001 return channel_level_option(lvlVote, CSFUNC_ARGS);
6004 static MODCMD_FUNC(chan_opt_inviteme)
6006 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6010 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6012 struct chanData *cData = channel->channel_info;
6013 int count = charOptions[option].count, idx;
6017 idx = atoi(argv[1]);
6019 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6021 reply("CSMSG_INVALID_NUMERIC", idx);
6022 /* Show possible values. */
6023 for(idx = 0; idx < count; idx++)
6024 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6028 cData->chOpts[option] = charOptions[option].values[idx].value;
6032 /* Find current option value. */
6035 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6039 /* Somehow, the option value is corrupt; reset it to the default. */
6040 cData->chOpts[option] = charOptions[option].default_value;
6045 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6049 static MODCMD_FUNC(chan_opt_protect)
6051 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6054 static MODCMD_FUNC(chan_opt_toys)
6056 return channel_multiple_option(chToys, CSFUNC_ARGS);
6059 static MODCMD_FUNC(chan_opt_ctcpreaction)
6061 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6064 static MODCMD_FUNC(chan_opt_topicrefresh)
6066 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6069 static struct svccmd_list set_shows_list;
6072 handle_svccmd_unbind(struct svccmd *target) {
6074 for(ii=0; ii<set_shows_list.used; ++ii)
6075 if(target == set_shows_list.list[ii])
6076 set_shows_list.used = 0;
6079 static CHANSERV_FUNC(cmd_set)
6081 struct svccmd *subcmd;
6085 /* Check if we need to (re-)initialize set_shows_list. */
6086 if(!set_shows_list.used)
6088 if(!set_shows_list.size)
6090 set_shows_list.size = chanserv_conf.set_shows->used;
6091 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6093 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6095 const char *name = chanserv_conf.set_shows->list[ii];
6096 sprintf(buf, "%s %s", argv[0], name);
6097 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6100 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6103 svccmd_list_append(&set_shows_list, subcmd);
6109 reply("CSMSG_CHANNEL_OPTIONS");
6110 for(ii = 0; ii < set_shows_list.used; ii++)
6112 subcmd = set_shows_list.list[ii];
6113 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6118 sprintf(buf, "%s %s", argv[0], argv[1]);
6119 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6122 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6125 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6127 reply("CSMSG_NO_ACCESS");
6133 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6137 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6139 struct userData *uData;
6141 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6144 reply("CSMSG_NOT_USER", channel->name);
6150 /* Just show current option value. */
6152 else if(enabled_string(argv[1]))
6154 uData->flags |= mask;
6156 else if(disabled_string(argv[1]))
6158 uData->flags &= ~mask;
6162 reply("MSG_INVALID_BINARY", argv[1]);
6166 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6170 static MODCMD_FUNC(user_opt_noautoop)
6172 struct userData *uData;
6174 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6177 reply("CSMSG_NOT_USER", channel->name);
6180 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6181 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6183 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6186 static MODCMD_FUNC(user_opt_autoinvite)
6188 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6190 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6192 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6195 static MODCMD_FUNC(user_opt_info)
6197 struct userData *uData;
6200 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6204 /* If they got past the command restrictions (which require access)
6205 * but fail this test, we have some fool with security override on.
6207 reply("CSMSG_NOT_USER", channel->name);
6214 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6215 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6217 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6220 bp = strcspn(infoline, "\001");
6223 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6228 if(infoline[0] == '*' && infoline[1] == 0)
6231 uData->info = strdup(infoline);
6234 reply("CSMSG_USET_INFO", uData->info);
6236 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6240 struct svccmd_list uset_shows_list;
6242 static CHANSERV_FUNC(cmd_uset)
6244 struct svccmd *subcmd;
6248 /* Check if we need to (re-)initialize uset_shows_list. */
6249 if(!uset_shows_list.used)
6253 "NoAutoOp", "AutoInvite", "Info"
6256 if(!uset_shows_list.size)
6258 uset_shows_list.size = ArrayLength(options);
6259 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6261 for(ii = 0; ii < ArrayLength(options); ii++)
6263 const char *name = options[ii];
6264 sprintf(buf, "%s %s", argv[0], name);
6265 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6268 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6271 svccmd_list_append(&uset_shows_list, subcmd);
6277 /* Do this so options are presented in a consistent order. */
6278 reply("CSMSG_USER_OPTIONS");
6279 for(ii = 0; ii < uset_shows_list.used; ii++)
6280 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6284 sprintf(buf, "%s %s", argv[0], argv[1]);
6285 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6288 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6292 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6295 static CHANSERV_FUNC(cmd_giveownership)
6297 struct handle_info *new_owner_hi;
6298 struct userData *new_owner;
6299 struct userData *curr_user;
6300 struct userData *invoker;
6301 struct chanData *cData = channel->channel_info;
6302 struct do_not_register *dnr;
6303 const char *confirm;
6305 unsigned short co_access;
6306 char reason[MAXLEN];
6309 curr_user = GetChannelAccess(cData, user->handle_info);
6310 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6311 if(!curr_user || (curr_user->access != UL_OWNER))
6313 struct userData *owner = NULL;
6314 for(curr_user = channel->channel_info->users;
6316 curr_user = curr_user->next)
6318 if(curr_user->access != UL_OWNER)
6322 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6329 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6331 char delay[INTERVALLEN];
6332 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6333 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6336 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6338 if(new_owner_hi == user->handle_info)
6340 reply("CSMSG_NO_TRANSFER_SELF");
6343 new_owner = GetChannelAccess(cData, new_owner_hi);
6348 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6352 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6356 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6358 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6361 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6362 if(!IsHelping(user))
6363 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6365 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6368 invoker = GetChannelUser(cData, user->handle_info);
6369 if(invoker->access <= UL_OWNER)
6371 confirm = make_confirmation_string(curr_user);
6372 if((argc < 3) || strcmp(argv[2], confirm))
6374 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6378 if(new_owner->access >= UL_COOWNER)
6379 co_access = new_owner->access;
6381 co_access = UL_COOWNER;
6382 new_owner->access = UL_OWNER;
6384 curr_user->access = co_access;
6385 cData->ownerTransfer = now;
6386 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6387 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6388 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6392 static CHANSERV_FUNC(cmd_suspend)
6394 struct handle_info *hi;
6395 struct userData *actor, *real_actor, *target;
6396 unsigned int override = 0;
6399 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6400 actor = GetChannelUser(channel->channel_info, user->handle_info);
6401 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6402 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6404 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6407 if(target->access >= actor->access)
6409 reply("MSG_USER_OUTRANKED", hi->handle);
6412 if(target->flags & USER_SUSPENDED)
6414 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6419 target->present = 0;
6422 if(!real_actor || target->access >= real_actor->access)
6423 override = CMD_LOG_OVERRIDE;
6424 target->flags |= USER_SUSPENDED;
6425 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6426 return 1 | override;
6429 static CHANSERV_FUNC(cmd_unsuspend)
6431 struct handle_info *hi;
6432 struct userData *actor, *real_actor, *target;
6433 unsigned int override = 0;
6436 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6437 actor = GetChannelUser(channel->channel_info, user->handle_info);
6438 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6439 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6441 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6444 if(target->access >= actor->access)
6446 reply("MSG_USER_OUTRANKED", hi->handle);
6449 if(!(target->flags & USER_SUSPENDED))
6451 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6454 if(!real_actor || target->access >= real_actor->access)
6455 override = CMD_LOG_OVERRIDE;
6456 target->flags &= ~USER_SUSPENDED;
6457 scan_user_presence(target, NULL);
6458 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6459 return 1 | override;
6462 static MODCMD_FUNC(cmd_deleteme)
6464 struct handle_info *hi;
6465 struct userData *target;
6466 const char *confirm_string;
6467 unsigned short access_level;
6470 hi = user->handle_info;
6471 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6473 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6476 if(target->access == UL_OWNER)
6478 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6481 confirm_string = make_confirmation_string(target);
6482 if((argc < 2) || strcmp(argv[1], confirm_string))
6484 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6487 access_level = target->access;
6488 channel_name = strdup(channel->name);
6489 del_channel_user(target, 1);
6490 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6495 static CHANSERV_FUNC(cmd_addvote)
6497 struct chanData *cData = channel->channel_info;
6498 struct userData *uData, *target;
6499 struct handle_info *hi;
6500 if (!cData) return 0;
6502 hi = user->handle_info;
6503 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6505 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6508 if(target->access < 300) {
6509 reply("CSMSG_NO_ACCESS");
6513 reply("CSMSG_ADDVOTE_FULL");
6517 msg = unsplit_string(argv + 1, argc - 1, NULL);
6518 cData->vote = strdup(msg);
6519 cData->vote_start=0;
6520 dict_delete(cData->vote_options);
6521 cData->vote_options = dict_new();
6522 dict_set_free_data(cData->vote_options, free_vote_options);
6523 for(uData = channel->channel_info->users; uData; uData = uData->next)
6528 reply("CSMSG_ADDVOTE_DONE");
6532 static CHANSERV_FUNC(cmd_delvote)
6534 struct chanData *cData = channel->channel_info;
6535 struct userData *target;
6536 struct handle_info *hi;
6537 if (!cData) return 0;
6538 hi = user->handle_info;
6539 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6541 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6544 if(target->access < 300) {
6545 reply("CSMSG_NO_ACCESS");
6549 reply("CSMSG_NO_VOTE");
6554 reply("CSMSG_DELVOTE_DONE");
6558 static CHANSERV_FUNC(cmd_addoption)
6560 struct chanData *cData = channel->channel_info;
6561 struct userData *target;
6562 struct handle_info *hi;
6563 if (!cData) return 0;
6565 hi = user->handle_info;
6566 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6568 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6571 if(target->access < 300) {
6572 reply("CSMSG_NO_ACCESS");
6576 reply("CSMSG_NO_VOTE");
6582 msg = unsplit_string(argv + 1, argc - 1, NULL);
6585 unsigned int lastid = 1;
6586 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6587 struct vote_option *cvOpt = iter_data(it);
6588 if(cvOpt->option_id > lastid)
6589 lastid = cvOpt->option_id;
6591 struct vote_option *vOpt;
6592 vOpt = calloc(1, sizeof(*vOpt));
6593 vOpt->name = strdup(msg);
6594 vOpt->option_id = (lastid + 1);
6596 sprintf(str,"%i",(lastid + 1));
6597 vOpt->option_str = strdup(str);
6599 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6601 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6605 static CHANSERV_FUNC(cmd_deloption)
6607 struct chanData *cData = channel->channel_info;
6608 struct userData *uData, *target;
6609 struct handle_info *hi;
6610 if (!cData) return 0;
6612 hi = user->handle_info;
6613 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6615 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6618 if(target->access < 300) {
6619 reply("CSMSG_NO_ACCESS");
6623 reply("CSMSG_NO_VOTE");
6626 if(cData->vote_start) {
6627 if(dict_size(cData->vote_options) < 3) {
6628 reply("CSMSG_VOTE_NEED_OPTIONS");
6633 int find_id = atoi(argv[1]);
6635 unsigned int found = 0;
6638 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6640 if (find_id == ii) {
6641 struct vote_option *vOpt = iter_data(it);
6642 found = vOpt->option_id;
6644 sprintf(str,"%i",vOpt->option_id);
6645 dict_remove(cData->vote_options, str);
6650 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6651 if(uData->votefor == found) {
6656 reply("CSMSG_DELOPTION_DONE");
6659 reply("CSMSG_DELOPTION_NONE");
6664 static CHANSERV_FUNC(cmd_vote)
6666 struct chanData *cData = channel->channel_info;
6667 struct userData *target;
6668 struct handle_info *hi;
6669 unsigned int votedfor = 0;
6670 char *votedfor_str = NULL;
6672 if (!cData || !cData->vote) {
6673 reply("CSMSG_NO_VOTE");
6676 if(argc > 1 && cData->vote_start) {
6677 hi = user->handle_info;
6678 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6680 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6683 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6684 reply("CSMSG_NO_ACCESS");
6688 reply("CSMSG_VOTE_VOTED");
6691 int find_id = atoi(argv[1]);
6694 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6696 if (find_id == ii) {
6697 struct vote_option *vOpt = iter_data(it);
6700 target->votefor = vOpt->option_id;
6701 votedfor = vOpt->option_id;
6702 votedfor_str = vOpt->name;
6706 reply("CSMSG_VOTE_INVALID");
6710 if (!cData->vote_start) {
6711 reply("CSMSG_VOTE_NOT_STARTED");
6713 reply("CSMSG_VOTE_QUESTION",cData->vote);
6715 unsigned int voteid = 0;
6718 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6719 struct vote_option *vOpt = iter_data(it);
6721 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6723 if(argc > 1 && cData->vote_start && votedfor_str) {
6724 reply("CSMSG_VOTE_DONE",votedfor_str);
6729 static CHANSERV_FUNC(cmd_startvote)
6731 struct chanData *cData = channel->channel_info;
6732 struct userData *target;
6733 struct handle_info *hi;
6734 if (!cData) return 0;
6735 hi = user->handle_info;
6736 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6738 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6741 if(target->access < 300) {
6742 reply("CSMSG_NO_ACCESS");
6746 reply("CSMSG_NO_VOTE");
6749 if(cData->vote_start) {
6750 reply("CSMSG_STARTVOTE_RUNNING");
6753 if(dict_size(cData->vote_options) < 2) {
6754 reply("CSMSG_VOTE_NEED_OPTIONS");
6757 cData->vote_start = 1;
6758 char response[MAXLEN];
6759 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6760 irc_privmsg(cmd->parent->bot, channel->name, response);
6761 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6762 irc_privmsg(cmd->parent->bot, channel->name, response);
6763 unsigned int voteid = 0;
6765 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6766 struct vote_option *vOpt = iter_data(it);
6768 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6769 irc_privmsg(cmd->parent->bot, channel->name, response);
6771 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6772 irc_privmsg(cmd->parent->bot, channel->name, response);
6773 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6774 irc_privmsg(cmd->parent->bot, channel->name, response);
6778 static CHANSERV_FUNC(cmd_endvote)
6780 struct chanData *cData = channel->channel_info;
6781 struct userData *target;
6782 struct handle_info *hi;
6783 if (!cData) return 0;
6784 hi = user->handle_info;
6785 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6787 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6790 if(target->access < 300) {
6791 reply("CSMSG_NO_ACCESS");
6795 reply("CSMSG_NO_VOTE");
6798 if(!cData->vote_start) {
6799 reply("CSMSG_ENDVOTE_STOPPED");
6802 cData->vote_start = 0;
6803 reply("CSMSG_ENDVOTE_DONE");
6807 static CHANSERV_FUNC(cmd_voteresults)
6809 struct chanData *cData = channel->channel_info;
6810 struct userData *target;
6811 struct handle_info *hi;
6812 if (!cData) return 0;
6814 reply("CSMSG_NO_VOTE");
6817 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6818 hi = user->handle_info;
6819 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6821 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6824 if(target->access < 300) {
6825 reply("CSMSG_NO_ACCESS");
6828 char response[MAXLEN];
6829 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6830 irc_privmsg(cmd->parent->bot, channel->name, response);
6831 unsigned int voteid = 0;
6833 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6834 struct vote_option *vOpt = iter_data(it);
6836 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6837 irc_privmsg(cmd->parent->bot, channel->name, response);
6840 reply("CSMSG_VOTE_QUESTION",cData->vote);
6841 unsigned int voteid = 0;
6843 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6844 struct vote_option *vOpt = iter_data(it);
6846 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6853 chanserv_refresh_topics(UNUSED_ARG(void *data))
6855 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6856 struct chanData *cData;
6859 for(cData = channelList; cData; cData = cData->next)
6861 if(IsSuspended(cData))
6863 opt = cData->chOpts[chTopicRefresh];
6866 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6869 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6870 cData->last_refresh = refresh_num;
6872 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6875 static CHANSERV_FUNC(cmd_unf)
6879 char response[MAXLEN];
6880 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6881 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6882 irc_privmsg(cmd->parent->bot, channel->name, response);
6885 reply("CSMSG_UNF_RESPONSE");
6889 static CHANSERV_FUNC(cmd_ping)
6893 char response[MAXLEN];
6894 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6895 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6896 irc_privmsg(cmd->parent->bot, channel->name, response);
6899 reply("CSMSG_PING_RESPONSE");
6903 static CHANSERV_FUNC(cmd_wut)
6907 char response[MAXLEN];
6908 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6909 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6910 irc_privmsg(cmd->parent->bot, channel->name, response);
6913 reply("CSMSG_WUT_RESPONSE");
6917 static CHANSERV_FUNC(cmd_8ball)
6919 unsigned int i, j, accum;
6924 for(i=1; i<argc; i++)
6925 for(j=0; argv[i][j]; j++)
6926 accum = (accum << 5) - accum + toupper(argv[i][j]);
6927 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6930 char response[MAXLEN];
6931 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6932 irc_privmsg(cmd->parent->bot, channel->name, response);
6935 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6939 static CHANSERV_FUNC(cmd_d)
6941 unsigned long sides, count, modifier, ii, total;
6942 char response[MAXLEN], *sep;
6946 if((count = strtoul(argv[1], &sep, 10)) < 1)
6956 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6957 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6961 else if((sep[0] == '-') && isdigit(sep[1]))
6962 modifier = strtoul(sep, NULL, 10);
6963 else if((sep[0] == '+') && isdigit(sep[1]))
6964 modifier = strtoul(sep+1, NULL, 10);
6971 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6976 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6979 for(total = ii = 0; ii < count; ++ii)
6980 total += (rand() % sides) + 1;
6983 if((count > 1) || modifier)
6985 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6986 sprintf(response, fmt, total, count, sides, modifier);
6990 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6991 sprintf(response, fmt, total, sides);
6994 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6996 send_message_type(4, user, cmd->parent->bot, "%s", response);
7000 static CHANSERV_FUNC(cmd_huggle)
7002 /* CTCP must be via PRIVMSG, never notice */
7004 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7006 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7011 chanserv_adjust_limit(void *data)
7013 struct mod_chanmode change;
7014 struct chanData *cData = data;
7015 struct chanNode *channel = cData->channel;
7018 if(IsSuspended(cData))
7021 cData->limitAdjusted = now;
7022 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7023 if(cData->modes.modes_set & MODE_LIMIT)
7025 if(limit > cData->modes.new_limit)
7026 limit = cData->modes.new_limit;
7027 else if(limit == cData->modes.new_limit)
7031 mod_chanmode_init(&change);
7032 change.modes_set = MODE_LIMIT;
7033 change.new_limit = limit;
7034 mod_chanmode_announce(chanserv, channel, &change);
7038 handle_new_channel(struct chanNode *channel)
7040 struct chanData *cData;
7042 if(!(cData = channel->channel_info))
7045 if(cData->modes.modes_set || cData->modes.modes_clear)
7046 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7048 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7049 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7052 void handle_new_channel_created(char *chan, struct userNode *user) {
7053 if(user->handle_info && chanserv_conf.new_channel_authed) {
7054 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7055 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7056 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7058 if(chanserv_conf.new_channel_msg)
7059 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7062 /* Welcome to my worst nightmare. Warning: Read (or modify)
7063 the code below at your own risk. */
7065 handle_join(struct modeNode *mNode)
7067 struct mod_chanmode change;
7068 struct userNode *user = mNode->user;
7069 struct chanNode *channel = mNode->channel;
7070 struct chanData *cData;
7071 struct userData *uData = NULL;
7072 struct banData *bData;
7073 struct handle_info *handle;
7074 unsigned int modes = 0, info = 0;
7078 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7081 cData = channel->channel_info;
7082 if(channel->members.used > cData->max) {
7083 cData->max = channel->members.used;
7084 cData->max_time = now;
7087 for(i = 0; i < channel->invited.used; i++)
7089 if(channel->invited.list[i] == user) {
7090 userList_remove(&channel->invited, user);
7094 /* Check for bans. If they're joining through a ban, one of two
7096 * 1: Join during a netburst, by riding the break. Kick them
7097 * unless they have ops or voice in the channel.
7098 * 2: They're allowed to join through the ban (an invite in
7099 * ircu2.10, or a +e on Hybrid, or something).
7100 * If they're not joining through a ban, and the banlist is not
7101 * full, see if they're on the banlist for the channel. If so,
7104 if(user->uplink->burst && !mNode->modes)
7107 for(ii = 0; ii < channel->banlist.used; ii++)
7109 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7111 /* Riding a netburst. Naughty. */
7112 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7118 mod_chanmode_init(&change);
7120 if(channel->banlist.used < MAXBANS)
7122 /* Not joining through a ban. */
7123 for(bData = cData->bans;
7124 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7125 bData = bData->next);
7129 char kick_reason[MAXLEN];
7130 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7132 bData->triggered = now;
7133 if(bData != cData->bans)
7135 /* Shuffle the ban to the head of the list. */
7137 bData->next->prev = bData->prev;
7139 bData->prev->next = bData->next;
7142 bData->next = cData->bans;
7145 cData->bans->prev = bData;
7146 cData->bans = bData;
7149 change.args[0].mode = MODE_BAN;
7150 change.args[0].u.hostmask = bData->mask;
7151 mod_chanmode_announce(chanserv, channel, &change);
7152 KickChannelUser(user, channel, chanserv, kick_reason);
7157 /* ChanServ will not modify the limits in join-flooded channels,
7158 or when there are enough slots left below the limit. */
7159 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7160 && !channel->join_flooded
7161 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7163 /* The user count has begun "bumping" into the channel limit,
7164 so set a timer to raise the limit a bit. Any previous
7165 timers are removed so three incoming users within the delay
7166 results in one limit change, not three. */
7168 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7169 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7172 if(channel->join_flooded)
7174 /* don't automatically give ops or voice during a join flood */
7176 else if(cData->lvlOpts[lvlGiveOps] == 0)
7177 modes |= MODE_CHANOP;
7178 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7179 modes |= MODE_VOICE;
7181 greeting = cData->greeting;
7182 if(user->handle_info)
7184 handle = user->handle_info;
7186 if(IsHelper(user) && !IsHelping(user))
7189 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7191 if(channel == chanserv_conf.support_channels.list[ii])
7193 HANDLE_SET_FLAG(user->handle_info, HELPING);
7199 uData = GetTrueChannelAccess(cData, handle);
7200 if(uData && !IsUserSuspended(uData))
7202 /* Ops and above were handled by the above case. */
7203 if(IsUserAutoOp(uData))
7205 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7206 modes |= MODE_CHANOP;
7207 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7208 modes |= MODE_VOICE;
7210 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7211 cData->visited = now;
7212 if(cData->user_greeting)
7213 greeting = cData->user_greeting;
7215 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7216 && ((now - uData->seen) >= chanserv_conf.info_delay)
7224 /* If user joining normally (not during burst), apply op or voice,
7225 * and send greeting/userinfo as appropriate.
7227 if(!user->uplink->burst)
7231 if(modes & MODE_CHANOP)
7232 modes &= ~MODE_VOICE;
7233 change.args[0].mode = modes;
7234 change.args[0].u.member = mNode;
7235 mod_chanmode_announce(chanserv, channel, &change);
7238 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7239 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7240 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7246 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7248 struct mod_chanmode change;
7249 struct userData *channel;
7250 unsigned int ii, jj;
7252 if(!user->handle_info)
7255 mod_chanmode_init(&change);
7257 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7259 struct chanNode *cn;
7260 struct modeNode *mn;
7261 if(IsUserSuspended(channel)
7262 || IsSuspended(channel->channel)
7263 || !(cn = channel->channel->channel))
7266 mn = GetUserMode(cn, user);
7269 if(!IsUserSuspended(channel)
7270 && IsUserAutoInvite(channel)
7271 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7273 && !user->uplink->burst)
7274 irc_invite(chanserv, user, cn);
7278 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7279 channel->channel->visited = now;
7281 if(IsUserAutoOp(channel))
7283 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7284 change.args[0].mode = MODE_CHANOP;
7285 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7286 change.args[0].mode = MODE_VOICE;
7288 change.args[0].mode = 0;
7289 change.args[0].u.member = mn;
7290 if(change.args[0].mode)
7291 mod_chanmode_announce(chanserv, cn, &change);
7294 channel->seen = now;
7295 channel->present = 1;
7298 for(ii = 0; ii < user->channels.used; ++ii)
7300 struct chanNode *chan = user->channels.list[ii]->channel;
7301 struct banData *ban;
7303 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7304 || !chan->channel_info
7305 || IsSuspended(chan->channel_info))
7307 for(jj = 0; jj < chan->banlist.used; ++jj)
7308 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7310 if(jj < chan->banlist.used)
7312 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7314 char kick_reason[MAXLEN];
7315 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7317 change.args[0].mode = MODE_BAN;
7318 change.args[0].u.hostmask = ban->mask;
7319 mod_chanmode_announce(chanserv, chan, &change);
7320 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7321 KickChannelUser(user, chan, chanserv, kick_reason);
7322 ban->triggered = now;
7327 if(IsSupportHelper(user))
7329 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7331 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7333 HANDLE_SET_FLAG(user->handle_info, HELPING);
7341 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7343 struct chanData *cData;
7344 struct userData *uData;
7346 cData = mn->channel->channel_info;
7347 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7350 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7352 /* Allow for a bit of padding so that the limit doesn't
7353 track the user count exactly, which could get annoying. */
7354 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7356 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7357 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7361 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7363 scan_user_presence(uData, mn->user);
7365 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7366 cData->visited = now;
7369 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7372 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7373 struct chanNode *channel;
7374 struct userNode *exclude;
7375 /* When looking at the channel that is being /part'ed, we
7376 * have to skip over the client that is leaving. For
7377 * other channels, we must not do that.
7379 channel = chanserv_conf.support_channels.list[ii];
7380 exclude = (channel == mn->channel) ? mn->user : NULL;
7381 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7384 if(ii == chanserv_conf.support_channels.used)
7385 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7390 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7392 struct userData *uData;
7394 if(!channel->channel_info || !kicker || IsService(kicker)
7395 || (kicker == victim) || IsSuspended(channel->channel_info)
7396 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7399 if(protect_user(victim, kicker, channel->channel_info))
7401 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7402 KickChannelUser(kicker, channel, chanserv, reason);
7405 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7410 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7412 struct chanData *cData;
7414 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7417 cData = channel->channel_info;
7418 if(bad_topic(channel, user, channel->topic))
7420 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7421 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7422 SetChannelTopic(channel, chanserv, old_topic, 1);
7423 else if(cData->topic)
7424 SetChannelTopic(channel, chanserv, cData->topic, 1);
7427 /* With topicsnarf, grab the topic and save it as the default topic. */
7428 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7431 cData->topic = strdup(channel->topic);
7437 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7439 struct mod_chanmode *bounce = NULL;
7440 unsigned int bnc, ii;
7443 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7446 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7447 && mode_lock_violated(&channel->channel_info->modes, change))
7449 char correct[MAXLEN];
7450 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7451 mod_chanmode_format(&channel->channel_info->modes, correct);
7452 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7454 for(ii = bnc = 0; ii < change->argc; ++ii)
7456 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7458 const struct userNode *victim = change->args[ii].u.member->user;
7459 if(!protect_user(victim, user, channel->channel_info))
7462 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7465 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7466 bounce->args[bnc].u.member = GetUserMode(channel, user);
7467 if(bounce->args[bnc].u.member)
7471 bounce->args[bnc].mode = MODE_CHANOP;
7472 bounce->args[bnc].u.member = change->args[ii].u.member;
7474 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7476 else if(change->args[ii].mode & MODE_CHANOP)
7478 const struct userNode *victim = change->args[ii].u.member->user;
7479 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7482 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7483 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7484 bounce->args[bnc].u.member = change->args[ii].u.member;
7487 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7489 const char *ban = change->args[ii].u.hostmask;
7490 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7493 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7494 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7495 bounce->args[bnc].u.hostmask = strdup(ban);
7497 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7502 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7503 mod_chanmode_announce(chanserv, channel, bounce);
7504 for(ii = 0; ii < change->argc; ++ii)
7505 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7506 free((char*)bounce->args[ii].u.hostmask);
7507 mod_chanmode_free(bounce);
7512 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7514 struct chanNode *channel;
7515 struct banData *bData;
7516 struct mod_chanmode change;
7517 unsigned int ii, jj;
7518 char kick_reason[MAXLEN];
7520 mod_chanmode_init(&change);
7522 change.args[0].mode = MODE_BAN;
7523 for(ii = 0; ii < user->channels.used; ++ii)
7525 channel = user->channels.list[ii]->channel;
7526 /* Need not check for bans if they're opped or voiced. */
7527 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7529 /* Need not check for bans unless channel registration is active. */
7530 if(!channel->channel_info || IsSuspended(channel->channel_info))
7532 /* Look for a matching ban already on the channel. */
7533 for(jj = 0; jj < channel->banlist.used; ++jj)
7534 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7536 /* Need not act if we found one. */
7537 if(jj < channel->banlist.used)
7539 /* Look for a matching ban in this channel. */
7540 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7542 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7544 change.args[0].u.hostmask = bData->mask;
7545 mod_chanmode_announce(chanserv, channel, &change);
7546 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7547 KickChannelUser(user, channel, chanserv, kick_reason);
7548 bData->triggered = now;
7549 break; /* we don't need to check any more bans in the channel */
7554 static void handle_rename(struct handle_info *handle, const char *old_handle)
7556 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7560 dict_remove2(handle_dnrs, old_handle, 1);
7561 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7562 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7567 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7569 struct userNode *h_user;
7571 if(handle->channels)
7573 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7574 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7576 while(handle->channels)
7577 del_channel_user(handle->channels, 1);
7582 handle_server_link(UNUSED_ARG(struct server *server))
7584 struct chanData *cData;
7586 for(cData = channelList; cData; cData = cData->next)
7588 if(!IsSuspended(cData))
7589 cData->may_opchan = 1;
7590 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7591 && !cData->channel->join_flooded
7592 && ((cData->channel->limit - cData->channel->members.used)
7593 < chanserv_conf.adjust_threshold))
7595 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7596 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7602 chanserv_conf_read(void)
7606 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7607 struct mod_chanmode *change;
7608 struct string_list *strlist;
7609 struct chanNode *chan;
7612 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7614 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7617 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7618 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7619 chanserv_conf.support_channels.used = 0;
7620 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7622 for(ii = 0; ii < strlist->used; ++ii)
7624 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7627 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7629 channelList_append(&chanserv_conf.support_channels, chan);
7632 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7635 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7638 chan = AddChannel(str, now, str2, NULL);
7640 channelList_append(&chanserv_conf.support_channels, chan);
7642 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7643 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7644 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7645 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7646 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7647 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7648 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7649 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7650 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7651 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7652 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7653 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7654 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7655 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7656 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7657 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7658 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7659 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7660 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7661 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7662 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7663 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7664 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7665 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7666 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7667 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7668 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7670 NickChange(chanserv, str, 0);
7671 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7672 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7673 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7674 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7675 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7676 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7677 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7678 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7679 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7680 chanserv_conf.max_owned = str ? atoi(str) : 5;
7681 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7682 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7683 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7684 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7685 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7686 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7687 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7688 chanserv_conf.new_channel_authed = str ? str : NULL;
7689 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7690 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7691 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7692 chanserv_conf.new_channel_msg = str ? str : NULL;
7693 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7696 safestrncpy(mode_line, str, sizeof(mode_line));
7697 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7698 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7699 && (change->argc < 2))
7701 chanserv_conf.default_modes = *change;
7702 mod_chanmode_free(change);
7704 free_string_list(chanserv_conf.set_shows);
7705 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7707 strlist = string_list_copy(strlist);
7710 static const char *list[] = {
7711 /* free form text */
7712 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7713 /* options based on user level */
7714 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7715 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7716 /* multiple choice options */
7717 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7718 /* binary options */
7719 "DynLimit", "NoDelete", "expire", "Vote",
7723 strlist = alloc_string_list(ArrayLength(list)-1);
7724 for(ii=0; list[ii]; ii++)
7725 string_list_append(strlist, strdup(list[ii]));
7727 chanserv_conf.set_shows = strlist;
7728 /* We don't look things up now, in case the list refers to options
7729 * defined by modules initialized after this point. Just mark the
7730 * function list as invalid, so it will be initialized.
7732 set_shows_list.used = 0;
7733 free_string_list(chanserv_conf.eightball);
7734 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7737 strlist = string_list_copy(strlist);
7741 strlist = alloc_string_list(4);
7742 string_list_append(strlist, strdup("Yes."));
7743 string_list_append(strlist, strdup("No."));
7744 string_list_append(strlist, strdup("Maybe so."));
7746 chanserv_conf.eightball = strlist;
7747 free_string_list(chanserv_conf.old_ban_names);
7748 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7750 strlist = string_list_copy(strlist);
7752 strlist = alloc_string_list(2);
7753 chanserv_conf.old_ban_names = strlist;
7754 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7755 off_channel = str ? atoi(str) : 0;
7759 chanserv_note_type_read(const char *key, struct record_data *rd)
7762 struct note_type *ntype;
7765 if(!(obj = GET_RECORD_OBJECT(rd)))
7767 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7770 if(!(ntype = chanserv_create_note_type(key)))
7772 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7776 /* Figure out set access */
7777 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7779 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7780 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7782 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7784 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7785 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7787 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7789 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7793 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7794 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7795 ntype->set_access.min_opserv = 0;
7798 /* Figure out visibility */
7799 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7800 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7801 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7802 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7803 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7804 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7805 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7806 ntype->visible_type = NOTE_VIS_ALL;
7808 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7810 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7811 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7815 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7817 struct vote_option *vOpt;
7820 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7822 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7826 vOpt = calloc(1, sizeof(*vOpt));
7827 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7828 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7829 vOpt->voted = str ? atoi(str) : 0;
7830 vOpt->option_id = str ? atoi(key) : 0;
7831 vOpt->option_str = strdup(key);
7832 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7836 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7838 struct handle_info *handle;
7839 struct userData *uData;
7840 char *seen, *inf, *flags, *voted, *votefor;
7841 unsigned long last_seen;
7842 unsigned short access_level;
7844 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7846 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7850 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7851 if(access_level > UL_OWNER)
7853 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7857 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7858 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7859 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7860 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7861 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7862 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7863 handle = get_handle_info(key);
7866 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7870 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7871 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7873 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7874 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7882 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7884 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7885 unsigned long set_time, triggered_time, expires_time;
7887 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7889 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7893 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7894 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7895 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7896 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7897 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7898 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7899 if (!reason || !owner)
7902 set_time = set ? strtoul(set, NULL, 0) : now;
7903 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7905 expires_time = strtoul(s_expires, NULL, 0);
7907 expires_time = set_time + atoi(s_duration);
7911 if(!reason || (expires_time && (expires_time < now)))
7914 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7917 static struct suspended *
7918 chanserv_read_suspended(dict_t obj)
7920 struct suspended *suspended = calloc(1, sizeof(*suspended));
7924 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7925 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7926 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7927 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7928 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7929 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7930 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7931 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7932 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7933 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7938 chanserv_channel_read(const char *key, struct record_data *hir)
7940 struct suspended *suspended;
7941 struct mod_chanmode *modes;
7942 struct chanNode *cNode;
7943 struct chanData *cData;
7944 struct dict *channel, *obj;
7945 char *str, *argv[10];
7949 channel = hir->d.object;
7951 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7954 cNode = AddChannel(key, now, NULL, NULL);
7957 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7960 cData = register_channel(cNode, str);
7963 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7967 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7969 enum levelOption lvlOpt;
7970 enum charOption chOpt;
7972 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7973 cData->flags = atoi(str);
7975 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7977 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7979 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7980 else if(levelOptions[lvlOpt].old_flag)
7982 if(cData->flags & levelOptions[lvlOpt].old_flag)
7983 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7985 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7989 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7991 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7993 cData->chOpts[chOpt] = str[0];
7996 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7998 enum levelOption lvlOpt;
7999 enum charOption chOpt;
8002 cData->flags = base64toint(str, 5);
8003 count = strlen(str += 5);
8004 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8007 if(levelOptions[lvlOpt].old_flag)
8009 if(cData->flags & levelOptions[lvlOpt].old_flag)
8010 lvl = levelOptions[lvlOpt].flag_value;
8012 lvl = levelOptions[lvlOpt].default_value;
8014 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8016 case 'c': lvl = UL_COOWNER; break;
8017 case 'm': lvl = UL_MASTER; break;
8018 case 'n': lvl = UL_OWNER+1; break;
8019 case 'o': lvl = UL_OP; break;
8020 case 'p': lvl = UL_PEON; break;
8021 case 'w': lvl = UL_OWNER; break;
8022 default: lvl = 0; break;
8024 cData->lvlOpts[lvlOpt] = lvl;
8026 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8027 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8030 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8032 cData->expiry = atoi(str);
8033 if(cData->expiry > 0) {
8034 if(cData->expiry > now) {
8035 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8037 timeq_add(1, chanserv_expire_channel, cData);
8044 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8046 suspended = chanserv_read_suspended(obj);
8047 cData->suspended = suspended;
8048 suspended->cData = cData;
8049 /* We could use suspended->expires and suspended->revoked to
8050 * set the CHANNEL_SUSPENDED flag, but we don't. */
8052 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8054 suspended = calloc(1, sizeof(*suspended));
8055 suspended->issued = 0;
8056 suspended->revoked = 0;
8057 suspended->suspender = strdup(str);
8058 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8059 suspended->expires = str ? atoi(str) : 0;
8060 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8061 suspended->reason = strdup(str ? str : "No reason");
8062 suspended->previous = NULL;
8063 cData->suspended = suspended;
8064 suspended->cData = cData;
8068 cData->flags &= ~CHANNEL_SUSPENDED;
8069 suspended = NULL; /* to squelch a warning */
8072 if(IsSuspended(cData)) {
8073 if(suspended->expires > now)
8074 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8075 else if(suspended->expires)
8076 cData->flags &= ~CHANNEL_SUSPENDED;
8079 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8080 struct mod_chanmode change;
8081 mod_chanmode_init(&change);
8083 change.args[0].mode = MODE_CHANOP;
8084 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8085 mod_chanmode_announce(chanserv, cNode, &change);
8088 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8089 cData->registered = str ? strtoul(str, NULL, 0) : now;
8090 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8091 cData->visited = str ? strtoul(str, NULL, 0) : now;
8092 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8093 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8094 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8095 cData->max = str ? atoi(str) : 0;
8096 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8097 cData->max_time = str ? atoi(str) : 0;
8098 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8099 cData->greeting = str ? strdup(str) : NULL;
8100 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8101 cData->user_greeting = str ? strdup(str) : NULL;
8102 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8103 cData->topic_mask = str ? strdup(str) : NULL;
8104 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8105 cData->topic = str ? strdup(str) : NULL;
8107 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8109 cData->vote = str ? strdup(str) : NULL;
8110 dict_delete(cData->vote_options);
8111 cData->vote_options = dict_new();
8112 dict_set_free_data(cData->vote_options, free_vote_options);
8113 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8114 cData->vote_start = str ? atoi(str) : 0;
8115 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8116 for(it = dict_first(obj); it; it = iter_next(it)) {
8117 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8121 if(!IsSuspended(cData)
8122 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8123 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8124 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8125 cData->modes = *modes;
8127 cData->modes.modes_set |= MODE_REGISTERED;
8128 if(cData->modes.argc > 1)
8129 cData->modes.argc = 1;
8130 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8131 mod_chanmode_free(modes);
8134 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8135 for(it = dict_first(obj); it; it = iter_next(it))
8136 user_read_helper(iter_key(it), iter_data(it), cData);
8138 if(!cData->users && !IsProtected(cData))
8140 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8141 unregister_channel(cData, "has empty user list.");
8145 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8146 for(it = dict_first(obj); it; it = iter_next(it))
8147 ban_read_helper(iter_key(it), iter_data(it), cData);
8149 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8150 for(it = dict_first(obj); it; it = iter_next(it))
8152 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8153 struct record_data *rd = iter_data(it);
8154 const char *note, *setter;
8156 if(rd->type != RECDB_OBJECT)
8158 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8162 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8164 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8166 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8170 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8171 if(!setter) setter = "<unknown>";
8172 chanserv_add_channel_note(cData, ntype, setter, note);
8180 chanserv_dnr_read(const char *key, struct record_data *hir)
8182 const char *setter, *reason, *str;
8183 struct do_not_register *dnr;
8184 unsigned long expiry;
8186 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8189 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8192 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8195 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8198 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8199 expiry = str ? strtoul(str, NULL, 0) : 0;
8200 if(expiry && expiry <= now)
8202 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8205 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8207 dnr->set = atoi(str);
8213 chanserv_saxdb_read(struct dict *database)
8215 struct dict *section;
8218 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8219 for(it = dict_first(section); it; it = iter_next(it))
8220 chanserv_note_type_read(iter_key(it), iter_data(it));
8222 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8223 for(it = dict_first(section); it; it = iter_next(it))
8224 chanserv_channel_read(iter_key(it), iter_data(it));
8226 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8227 for(it = dict_first(section); it; it = iter_next(it))
8228 chanserv_dnr_read(iter_key(it), iter_data(it));
8234 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8236 int high_present = 0;
8237 saxdb_start_record(ctx, KEY_USERS, 1);
8238 for(; uData; uData = uData->next)
8240 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8242 saxdb_start_record(ctx, uData->handle->handle, 0);
8243 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8244 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8246 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8247 if(uData->channel->vote && uData->voted)
8248 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8249 if(uData->channel->vote && uData->votefor)
8250 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8252 saxdb_write_string(ctx, KEY_INFO, uData->info);
8253 saxdb_end_record(ctx);
8255 saxdb_end_record(ctx);
8256 return high_present;
8260 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8264 saxdb_start_record(ctx, KEY_BANS, 1);
8265 for(; bData; bData = bData->next)
8267 saxdb_start_record(ctx, bData->mask, 0);
8268 saxdb_write_int(ctx, KEY_SET, bData->set);
8269 if(bData->triggered)
8270 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8272 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8274 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8276 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8277 saxdb_end_record(ctx);
8279 saxdb_end_record(ctx);
8283 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8285 saxdb_start_record(ctx, name, 0);
8286 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8287 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8289 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8291 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8293 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8295 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8296 saxdb_end_record(ctx);
8300 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8304 enum levelOption lvlOpt;
8305 enum charOption chOpt;
8308 saxdb_start_record(ctx, channel->channel->name, 1);
8310 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8311 saxdb_write_int(ctx, KEY_MAX, channel->max);
8312 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8314 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8315 if(channel->registrar)
8316 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8317 if(channel->greeting)
8318 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8319 if(channel->user_greeting)
8320 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8321 if(channel->topic_mask)
8322 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8323 if(channel->suspended)
8324 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8326 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8329 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8330 if(channel->vote_start)
8331 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8332 if (dict_size(channel->vote_options)) {
8333 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8334 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8335 struct vote_option *vOpt = iter_data(it);
8337 sprintf(str,"%i",vOpt->option_id);
8338 saxdb_start_record(ctx, str, 0);
8340 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8342 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8343 saxdb_end_record(ctx);
8345 saxdb_end_record(ctx);
8349 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8350 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8351 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8352 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8353 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8355 buf[0] = channel->chOpts[chOpt];
8357 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8359 saxdb_end_record(ctx);
8361 if(channel->modes.modes_set || channel->modes.modes_clear)
8363 mod_chanmode_format(&channel->modes, buf);
8364 saxdb_write_string(ctx, KEY_MODES, buf);
8367 high_present = chanserv_write_users(ctx, channel->users);
8368 chanserv_write_bans(ctx, channel->bans);
8370 if(dict_size(channel->notes))
8374 saxdb_start_record(ctx, KEY_NOTES, 1);
8375 for(it = dict_first(channel->notes); it; it = iter_next(it))
8377 struct note *note = iter_data(it);
8378 saxdb_start_record(ctx, iter_key(it), 0);
8379 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8380 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8381 saxdb_end_record(ctx);
8383 saxdb_end_record(ctx);
8386 if(channel->ownerTransfer)
8387 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8388 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8389 saxdb_end_record(ctx);
8393 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8397 saxdb_start_record(ctx, ntype->name, 0);
8398 switch(ntype->set_access_type)
8400 case NOTE_SET_CHANNEL_ACCESS:
8401 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8403 case NOTE_SET_CHANNEL_SETTER:
8404 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8406 case NOTE_SET_PRIVILEGED: default:
8407 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8410 switch(ntype->visible_type)
8412 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8413 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8414 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8416 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8417 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8418 saxdb_end_record(ctx);
8422 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8424 struct do_not_register *dnr;
8425 dict_iterator_t it, next;
8427 for(it = dict_first(dnrs); it; it = next)
8429 next = iter_next(it);
8430 dnr = iter_data(it);
8431 if(dnr->expires && dnr->expires <= now)
8433 dict_remove(dnrs, iter_key(it));
8436 saxdb_start_record(ctx, dnr->chan_name, 0);
8438 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8440 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8441 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8442 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8443 saxdb_end_record(ctx);
8448 chanserv_saxdb_write(struct saxdb_context *ctx)
8451 struct chanData *channel;
8454 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8455 for(it = dict_first(note_types); it; it = iter_next(it))
8456 chanserv_write_note_type(ctx, iter_data(it));
8457 saxdb_end_record(ctx);
8460 saxdb_start_record(ctx, KEY_DNR, 1);
8461 write_dnrs_helper(ctx, handle_dnrs);
8462 write_dnrs_helper(ctx, plain_dnrs);
8463 write_dnrs_helper(ctx, mask_dnrs);
8464 saxdb_end_record(ctx);
8467 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8468 for(channel = channelList; channel; channel = channel->next)
8469 chanserv_write_channel(ctx, channel);
8470 saxdb_end_record(ctx);
8476 chanserv_db_cleanup(void) {
8478 unreg_part_func(handle_part);
8480 unregister_channel(channelList, "terminating.");
8481 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8482 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8483 free(chanserv_conf.support_channels.list);
8484 dict_delete(handle_dnrs);
8485 dict_delete(plain_dnrs);
8486 dict_delete(mask_dnrs);
8487 dict_delete(note_types);
8488 free_string_list(chanserv_conf.eightball);
8489 free_string_list(chanserv_conf.old_ban_names);
8490 free_string_list(chanserv_conf.set_shows);
8491 free(set_shows_list.list);
8492 free(uset_shows_list.list);
8495 struct userData *helper = helperList;
8496 helperList = helperList->next;
8501 #if defined(GCC_VARMACROS)
8502 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8503 #elif defined(C99_VARMACROS)
8504 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8506 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8507 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8510 init_chanserv(const char *nick)
8512 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8513 conf_register_reload(chanserv_conf_read);
8517 reg_server_link_func(handle_server_link);
8518 reg_new_channel_func(handle_new_channel);
8519 reg_join_func(handle_join);
8520 reg_part_func(handle_part);
8521 reg_kick_func(handle_kick);
8522 reg_topic_func(handle_topic);
8523 reg_mode_change_func(handle_mode);
8524 reg_nick_change_func(handle_nick_change);
8525 reg_auth_func(handle_auth);
8528 reg_handle_rename_func(handle_rename);
8529 reg_unreg_func(handle_unreg);
8531 handle_dnrs = dict_new();
8532 dict_set_free_data(handle_dnrs, free);
8533 plain_dnrs = dict_new();
8534 dict_set_free_data(plain_dnrs, free);
8535 mask_dnrs = dict_new();
8536 dict_set_free_data(mask_dnrs, free);
8538 reg_svccmd_unbind_func(handle_svccmd_unbind);
8539 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8540 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8541 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8542 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8543 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8544 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8545 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8546 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8547 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8548 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8549 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8550 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8551 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8553 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8554 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8556 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8557 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8558 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8559 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8560 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8562 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8563 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8564 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8565 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8566 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8568 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8569 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8570 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8571 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8573 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8574 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8575 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8576 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8577 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8578 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8579 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8580 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8582 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8583 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8584 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8585 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8586 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8587 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8588 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8589 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8590 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8591 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8592 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8593 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8594 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8595 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8596 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8598 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8599 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8600 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8601 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8602 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8604 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8605 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8607 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8608 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8609 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8610 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8611 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8612 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8613 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8614 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8615 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8616 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8617 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8619 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8620 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8622 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8623 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8624 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8625 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8627 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8628 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8629 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8630 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8631 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8633 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8634 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8635 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8636 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8637 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8638 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8640 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8641 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8642 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8643 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8644 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8645 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8646 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8647 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8649 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8651 /* Channel options */
8652 DEFINE_CHANNEL_OPTION(defaulttopic);
8653 DEFINE_CHANNEL_OPTION(topicmask);
8654 DEFINE_CHANNEL_OPTION(greeting);
8655 DEFINE_CHANNEL_OPTION(usergreeting);
8656 DEFINE_CHANNEL_OPTION(modes);
8657 DEFINE_CHANNEL_OPTION(enfops);
8658 DEFINE_CHANNEL_OPTION(giveops);
8659 DEFINE_CHANNEL_OPTION(protect);
8660 DEFINE_CHANNEL_OPTION(enfmodes);
8661 DEFINE_CHANNEL_OPTION(enftopic);
8662 DEFINE_CHANNEL_OPTION(pubcmd);
8663 DEFINE_CHANNEL_OPTION(givevoice);
8664 DEFINE_CHANNEL_OPTION(userinfo);
8665 DEFINE_CHANNEL_OPTION(dynlimit);
8666 DEFINE_CHANNEL_OPTION(topicsnarf);
8667 DEFINE_CHANNEL_OPTION(vote);
8668 DEFINE_CHANNEL_OPTION(nodelete);
8669 DEFINE_CHANNEL_OPTION(toys);
8670 DEFINE_CHANNEL_OPTION(setters);
8671 DEFINE_CHANNEL_OPTION(topicrefresh);
8672 DEFINE_CHANNEL_OPTION(ctcpusers);
8673 DEFINE_CHANNEL_OPTION(ctcpreaction);
8674 DEFINE_CHANNEL_OPTION(inviteme);
8675 DEFINE_CHANNEL_OPTION(unreviewed);
8676 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8677 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8678 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8680 DEFINE_CHANNEL_OPTION(offchannel);
8681 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8683 /* Alias set topic to set defaulttopic for compatibility. */
8684 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8687 DEFINE_USER_OPTION(noautoop);
8688 DEFINE_USER_OPTION(autoinvite);
8689 DEFINE_USER_OPTION(info);
8691 /* Alias uset autovoice to uset autoop. */
8692 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8694 note_types = dict_new();
8695 dict_set_free_data(note_types, chanserv_deref_note_type);
8698 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8699 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8700 service_register(chanserv)->trigger = '!';
8701 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8703 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8705 if(chanserv_conf.channel_expire_frequency)
8706 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8708 if(chanserv_conf.dnr_expire_frequency)
8709 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8711 if(chanserv_conf.refresh_period)
8713 unsigned long next_refresh;
8714 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8715 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8718 reg_exit_func(chanserv_db_cleanup);
8719 message_register_table(msgtab);